diff options
Diffstat (limited to 'spec/ruby/optional/capi')
77 files changed, 5405 insertions, 1400 deletions
diff --git a/spec/ruby/optional/capi/array_spec.rb b/spec/ruby/optional/capi/array_spec.rb index 8d003fb2b1..0b997f774c 100644 --- a/spec/ruby/optional/capi/array_spec.rb +++ b/spec/ruby/optional/capi/array_spec.rb @@ -8,7 +8,7 @@ describe :rb_ary_new2, shared: true do end it "raises an ArgumentError when the given argument is negative" do - -> { @s.send(@method, -1) }.should raise_error(ArgumentError) + -> { @s.send(@method, -1) }.should.raise(ArgumentError) end end @@ -84,7 +84,7 @@ describe "C-API Array function" do end it "raises a FrozenError if the array is frozen" do - -> { @s.rb_ary_cat([].freeze, 1) }.should raise_error(FrozenError) + -> { @s.rb_ary_cat([].freeze, 1) }.should.raise(FrozenError) end end @@ -120,7 +120,7 @@ describe "C-API Array function" do it "returns the original array" do a = [1,2,3] - @s.rb_ary_reverse(a).should equal(a) + @s.rb_ary_reverse(a).should.equal?(a) end end @@ -131,7 +131,7 @@ describe "C-API Array function" do end it "raises a FrozenError if the array is frozen" do - -> { @s.rb_ary_rotate([].freeze, 1) }.should raise_error(FrozenError) + -> { @s.rb_ary_rotate([].freeze, 1) }.should.raise(FrozenError) end end @@ -166,7 +166,7 @@ describe "C-API Array function" do b = @s.rb_ary_dup(a) b.should == a - b.should_not equal(a) + b.should_not.equal?(a) end end @@ -221,7 +221,7 @@ describe "C-API Array function" do it "raises an IndexError if the negative index is greater than the length" do a = [1, 2, 3] - -> { @s.rb_ary_store(a, -10, 5) }.should raise_error(IndexError) + -> { @s.rb_ary_store(a, -10, 5) }.should.raise(IndexError) end it "enlarges the array as needed" do @@ -232,7 +232,7 @@ describe "C-API Array function" do it "raises a FrozenError if the array is frozen" do a = [1, 2, 3].freeze - -> { @s.rb_ary_store(a, 1, 5) }.should raise_error(FrozenError) + -> { @s.rb_ary_store(a, 1, 5) }.should.raise(FrozenError) end end @@ -307,11 +307,11 @@ describe "C-API Array function" do describe "rb_ary_includes" do it "returns true if the array includes the element" do - @s.rb_ary_includes([1, 2, 3], 2).should be_true + @s.rb_ary_includes([1, 2, 3], 2).should == true end it "returns false if the array does not include the element" do - @s.rb_ary_includes([1, 2, 3], 4).should be_false + @s.rb_ary_includes([1, 2, 3], 4).should == false end end @@ -322,7 +322,7 @@ describe "C-API Array function" do end it "returns nil for an out of range index" do - @s.rb_ary_aref([1, 2, 3], 6).should be_nil + @s.rb_ary_aref([1, 2, 3], 6).should == nil end it "returns a new array where the first argument is the index and the second is the length" do @@ -335,7 +335,7 @@ describe "C-API Array function" do end it "returns nil when the start of a range is out of bounds" do - @s.rb_ary_aref([1, 2, 3, 4], 6..10).should be_nil + @s.rb_ary_aref([1, 2, 3, 4], 6..10).should == nil end it "returns an empty array when the start of a range equals the last element" do @@ -343,21 +343,57 @@ describe "C-API Array function" do end end - describe "rb_iterate" do + ruby_version_is ""..."4.0" do + describe "rb_iterate" do + it "calls an callback function as a block passed to an method" do + s = [1,2,3,4] + s2 = @s.rb_iterate(s) + + s2.should == s + + # Make sure they're different objects + s2.equal?(s).should == false + end + + it "calls a function with the other function available as a block" do + h = {a: 1, b: 2} + + @s.rb_iterate_each_pair(h).sort.should == [1,2] + end + + it "calls a function which can yield into the original block" do + s2 = [] + + o = Object.new + def o.each + yield 1 + yield 2 + yield 3 + yield 4 + end + + @s.rb_iterate_then_yield(o) { |x| s2 << x } + + s2.should == [1,2,3,4] + end + end + end + + describe "rb_block_call" do it "calls an callback function as a block passed to an method" do s = [1,2,3,4] - s2 = @s.rb_iterate(s) + s2 = @s.rb_block_call(s) s2.should == s # Make sure they're different objects - s2.equal?(s).should be_false + s2.equal?(s).should == false end it "calls a function with the other function available as a block" do h = {a: 1, b: 2} - @s.rb_iterate_each_pair(h).sort.should == [1,2] + @s.rb_block_call_each_pair(h).sort.should == [1,2] end it "calls a function which can yield into the original block" do @@ -371,7 +407,7 @@ describe "C-API Array function" do yield 4 end - @s.rb_iterate_then_yield(o) { |x| s2 << x } + @s.rb_block_call_then_yield(o) { |x| s2 << x } s2.should == [1,2,3,4] end @@ -386,7 +422,7 @@ describe "C-API Array function" do it "returns nil if the element is not in the array" do ary = [1, 2, 3, 4] - @s.rb_ary_delete(ary, 5).should be_nil + @s.rb_ary_delete(ary, 5).should == nil ary.should == [1, 2, 3, 4] end end @@ -401,7 +437,7 @@ describe "C-API Array function" do it "freezes the object exactly like Kernel#freeze" do ary = [1,2] @s.rb_ary_freeze(ary) - ary.frozen?.should be_true + ary.frozen?.should == true end end @@ -421,12 +457,12 @@ describe "C-API Array function" do end it "returns nil if the index is out of bounds" do - @s.rb_ary_delete_at(@array, 4).should be_nil + @s.rb_ary_delete_at(@array, 4).should == nil @array.should == [1, 2, 3, 4] end it "returns nil if the negative index is out of bounds" do - @s.rb_ary_delete_at(@array, -5).should be_nil + @s.rb_ary_delete_at(@array, -5).should == nil @array.should == [1, 2, 3, 4] end end @@ -437,7 +473,7 @@ describe "C-API Array function" do it "returns the given array" do array = [1, 2, 3] - @s.rb_ary_to_ary(array).should equal(array) + @s.rb_ary_to_ary(array).should.equal?(array) end end @@ -483,7 +519,7 @@ describe "C-API Array function" do end it "returns nil if the begin index is out of bound" do - @s.rb_ary_subseq([1, 2, 3, 4, 5], 6, 3).should be_nil + @s.rb_ary_subseq([1, 2, 3, 4, 5], 6, 3).should == nil end it "returns the existing subsequence of the length is out of bounds" do @@ -491,7 +527,7 @@ describe "C-API Array function" do end it "returns nil if the size is negative" do - @s.rb_ary_subseq([1, 2, 3, 4, 5], 1, -1).should be_nil + @s.rb_ary_subseq([1, 2, 3, 4, 5], 1, -1).should == nil end end end diff --git a/spec/ruby/optional/capi/bignum_spec.rb b/spec/ruby/optional/capi/bignum_spec.rb index cde929af28..ae7552b3f5 100644 --- a/spec/ruby/optional/capi/bignum_spec.rb +++ b/spec/ruby/optional/capi/bignum_spec.rb @@ -7,21 +7,23 @@ def ensure_bignum(n) n end -full_range_longs = (fixnum_max == 2**(0.size * 8 - 1) - 1) +full_range_longs = (fixnum_max == max_long) +max_ulong = begin + require 'rbconfig/sizeof' + RbConfig::LIMITS['ULONG_MAX'] +rescue LoadError + nil +end +# If the system doesn't offer ULONG_MAX, assume 2's complement and derive it +# from LONG_MAX. +max_ulong ||= 2 * (max_long + 1) - 1 describe "CApiBignumSpecs" do before :each do @s = CApiBignumSpecs.new - - if full_range_longs - @max_long = 2**(0.size * 8 - 1) - 1 - @min_long = -@max_long - 1 - @max_ulong = ensure_bignum(2**(0.size * 8) - 1) - else - @max_long = ensure_bignum(2**(0.size * 8 - 1) - 1) - @min_long = ensure_bignum(-@max_long - 1) - @max_ulong = ensure_bignum(2**(0.size * 8) - 1) - end + @max_long = max_long + @min_long = min_long + @max_ulong = ensure_bignum(max_ulong) end describe "rb_big2long" do @@ -33,8 +35,8 @@ describe "CApiBignumSpecs" do end it "raises RangeError if passed Bignum overflow long" do - -> { @s.rb_big2long(ensure_bignum(@max_long + 1)) }.should raise_error(RangeError) - -> { @s.rb_big2long(ensure_bignum(@min_long - 1)) }.should raise_error(RangeError) + -> { @s.rb_big2long(ensure_bignum(@max_long + 1)) }.should.raise(RangeError) + -> { @s.rb_big2long(ensure_bignum(@min_long - 1)) }.should.raise(RangeError) end end @@ -47,8 +49,8 @@ describe "CApiBignumSpecs" do end it "raises RangeError if passed Bignum overflow long" do - -> { @s.rb_big2ll(ensure_bignum(@max_long << 40)) }.should raise_error(RangeError) - -> { @s.rb_big2ll(ensure_bignum(@min_long << 40)) }.should raise_error(RangeError) + -> { @s.rb_big2ll(ensure_bignum(@max_long << 40)) }.should.raise(RangeError) + -> { @s.rb_big2ll(ensure_bignum(@min_long << 40)) }.should.raise(RangeError) end end @@ -65,8 +67,8 @@ describe "CApiBignumSpecs" do end it "raises RangeError if passed Bignum overflow long" do - -> { @s.rb_big2ulong(ensure_bignum(@max_ulong + 1)) }.should raise_error(RangeError) - -> { @s.rb_big2ulong(ensure_bignum(@min_long - 1)) }.should raise_error(RangeError) + -> { @s.rb_big2ulong(ensure_bignum(@max_ulong + 1)) }.should.raise(RangeError) + -> { @s.rb_big2ulong(ensure_bignum(@min_long - 1)) }.should.raise(RangeError) end end @@ -123,7 +125,7 @@ describe "CApiBignumSpecs" do val.should == @max_ulong end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "packs max_ulong into 2 ulongs to allow sign bit" do val = @s.rb_big_pack_length(@max_ulong) val.should == 2 @@ -212,13 +214,13 @@ describe "CApiBignumSpecs" do it "raises FloatDomainError for Infinity values" do inf = 1.0 / 0 - -> { @s.rb_dbl2big(inf) }.should raise_error(FloatDomainError) + -> { @s.rb_dbl2big(inf) }.should.raise(FloatDomainError) end it "raises FloatDomainError for NaN values" do nan = 0.0 / 0 - -> { @s.rb_dbl2big(nan) }.should raise_error(FloatDomainError) + -> { @s.rb_dbl2big(nan) }.should.raise(FloatDomainError) end end end diff --git a/spec/ruby/optional/capi/binding_spec.rb b/spec/ruby/optional/capi/binding_spec.rb index 966d650c46..1edd20b8f3 100644 --- a/spec/ruby/optional/capi/binding_spec.rb +++ b/spec/ruby/optional/capi/binding_spec.rb @@ -8,12 +8,9 @@ describe "CApiBindingSpecs" do end describe "Kernel#binding" do - it "gives the top-most Ruby binding when called from C" do + it "raises when called from C" do foo = 14 - b = @b.get_binding - b.local_variable_get(:foo).should == 14 - b.local_variable_set :foo, 12 - foo.should == 12 + -> { @b.get_binding }.should.raise(RuntimeError) end end end diff --git a/spec/ruby/optional/capi/class_spec.rb b/spec/ruby/optional/capi/class_spec.rb index c2424668b9..07e9e42ad1 100644 --- a/spec/ruby/optional/capi/class_spec.rb +++ b/spec/ruby/optional/capi/class_spec.rb @@ -11,7 +11,8 @@ autoload :ClassIdUnderAutoload, "#{object_path}/class_id_under_autoload_spec" describe :rb_path_to_class, shared: true do it "returns a class or module from a scoped String" do - @s.send(@method, "CApiClassSpecs::A::B").should equal(CApiClassSpecs::A::B) + @s.send(@method, "CApiClassSpecs::A::B").should.equal?(CApiClassSpecs::A::B) + @s.send(@method, "CApiClassSpecs::A::M").should.equal?(CApiClassSpecs::A::M) end it "resolves autoload constants" do @@ -19,19 +20,21 @@ describe :rb_path_to_class, shared: true do end it "raises an ArgumentError if a constant in the path does not exist" do - -> { @s.send(@method, "CApiClassSpecs::NotDefined::B") }.should raise_error(ArgumentError) + -> { @s.send(@method, "CApiClassSpecs::NotDefined::B") }.should.raise(ArgumentError) end it "raises an ArgumentError if the final constant does not exist" do - -> { @s.send(@method, "CApiClassSpecs::NotDefined") }.should raise_error(ArgumentError) + -> { @s.send(@method, "CApiClassSpecs::NotDefined") }.should.raise(ArgumentError) end it "raises a TypeError if the constant is not a class or module" do - -> { @s.send(@method, "CApiClassSpecs::A::C") }.should raise_error(TypeError) + -> { + @s.send(@method, "CApiClassSpecs::A::C") + }.should.raise(TypeError, 'CApiClassSpecs::A::C does not refer to class/module') end it "raises an ArgumentError even if a constant in the path exists on toplevel" do - -> { @s.send(@method, "CApiClassSpecs::Object") }.should raise_error(ArgumentError) + -> { @s.send(@method, "CApiClassSpecs::Object") }.should.raise(ArgumentError) end end @@ -43,29 +46,31 @@ describe "C-API Class function" do describe "rb_class_instance_methods" do it "returns the public and protected methods of self and its ancestors" do methods = @s.rb_class_instance_methods(ModuleSpecs::Basic) - methods.should include(:protected_module, :public_module) + methods.should.include?(:protected_module) + methods.should.include?(:public_module) methods = @s.rb_class_instance_methods(ModuleSpecs::Basic, true) - methods.should include(:protected_module, :public_module) + methods.should.include?(:protected_module) + methods.should.include?(:public_module) end it "when passed false as a parameter, returns the instance methods of the class" do methods = @s.rb_class_instance_methods(ModuleSpecs::Child, false) - methods.should include(:protected_child, :public_child) + methods.to_set.should >= Set[:protected_child, :public_child] end end describe "rb_class_public_instance_methods" do it "returns a list of public methods in module and its ancestors" do methods = @s.rb_class_public_instance_methods(ModuleSpecs::CountsChild) - methods.should include(:public_3) - methods.should include(:public_2) - methods.should include(:public_1) + methods.should.include?(:public_3) + methods.should.include?(:public_2) + methods.should.include?(:public_1) methods = @s.rb_class_public_instance_methods(ModuleSpecs::CountsChild, true) - methods.should include(:public_3) - methods.should include(:public_2) - methods.should include(:public_1) + methods.should.include?(:public_3) + methods.should.include?(:public_2) + methods.should.include?(:public_1) end it "when passed false as a parameter, should return only methods defined in that module" do @@ -76,14 +81,14 @@ describe "C-API Class function" do describe "rb_class_protected_instance_methods" do it "returns a list of protected methods in module and its ancestors" do methods = @s.rb_class_protected_instance_methods(ModuleSpecs::CountsChild) - methods.should include(:protected_3) - methods.should include(:protected_2) - methods.should include(:protected_1) + methods.should.include?(:protected_3) + methods.should.include?(:protected_2) + methods.should.include?(:protected_1) methods = @s.rb_class_protected_instance_methods(ModuleSpecs::CountsChild, true) - methods.should include(:protected_3) - methods.should include(:protected_2) - methods.should include(:protected_1) + methods.should.include?(:protected_3) + methods.should.include?(:protected_2) + methods.should.include?(:protected_1) end it "when passed false as a parameter, should return only methods defined in that module" do @@ -105,24 +110,42 @@ describe "C-API Class function" do describe "rb_class_new_instance" do it "allocates and initializes a new object" do - o = @s.rb_class_new_instance(0, nil, CApiClassSpecs::Alloc) + o = @s.rb_class_new_instance([], CApiClassSpecs::Alloc) o.class.should == CApiClassSpecs::Alloc - o.initialized.should be_true + o.initialized.should == true end it "passes arguments to the #initialize method" do - o = @s.rb_class_new_instance(2, [:one, :two], CApiClassSpecs::Alloc) + o = @s.rb_class_new_instance([:one, :two], CApiClassSpecs::Alloc) o.arguments.should == [:one, :two] end end + describe "rb_class_new_instance_kw" do + it "passes arguments and keywords to the #initialize method" do + obj = @s.rb_class_new_instance_kw([{pos: 1}, {kw: 2}], CApiClassSpecs::KeywordAlloc) + obj.args.should == [{pos: 1}] + obj.kwargs.should == {kw: 2} + + obj = @s.rb_class_new_instance_kw([{}], CApiClassSpecs::KeywordAlloc) + obj.args.should == [] + obj.kwargs.should == {} + end + + it "raises TypeError if the last argument is not a Hash" do + -> { + @s.rb_class_new_instance_kw([42], CApiClassSpecs::KeywordAlloc) + }.should.raise(TypeError, 'no implicit conversion of Integer into Hash') + end + end + describe "rb_include_module" do it "includes a module into a class" do c = Class.new o = c.new - -> { o.included? }.should raise_error(NameError) + -> { o.included? }.should.raise(NameError) @s.rb_include_module(c, CApiClassSpecs::M) - o.included?.should be_true + o.included?.should == true end end @@ -134,12 +157,12 @@ describe "C-API Class function" do it "defines an attr_reader when passed true, false" do @s.rb_define_attr(CApiClassSpecs::Attr, :foo, true, false) @a.foo.should == 1 - -> { @a.foo = 5 }.should raise_error(NameError) + -> { @a.foo = 5 }.should.raise(NameError) end it "defines an attr_writer when passed false, true" do @s.rb_define_attr(CApiClassSpecs::Attr, :bar, false, true) - -> { @a.bar }.should raise_error(NameError) + -> { @a.bar }.should.raise(NameError) @a.bar = 5 @a.instance_variable_get(:@bar).should == 5 end @@ -162,7 +185,7 @@ describe "C-API Class function" do it "calls the method in the superclass with the correct self" do @s.define_call_super_method CApiClassSpecs::SubSelf, "call_super_method" obj = CApiClassSpecs::SubSelf.new - obj.call_super_method.should equal obj + obj.call_super_method.should.equal? obj end it "calls the method in the superclass through two native levels" do @@ -173,23 +196,17 @@ describe "C-API Class function" do end end - describe "rb_define_method" do - it "defines a method taking variable arguments as a C array if the argument count is -1" do - @s.rb_method_varargs_1(1, 3, 7, 4).should == [1, 3, 7, 4] - end - - it "defines a method taking variable arguments as a Ruby array if the argument count is -2" do - @s.rb_method_varargs_2(1, 3, 7, 4).should == [1, 3, 7, 4] - end - end - describe "rb_class2name" do it "returns the class name" do @s.rb_class2name(CApiClassSpecs).should == "CApiClassSpecs" end it "returns a string for an anonymous class" do - @s.rb_class2name(Class.new).should be_kind_of(String) + @s.rb_class2name(Class.new).should.is_a?(String) + end + + it "returns a string beginning with # for an anonymous class" do + @s.rb_class2name(Struct.new(:x, :y).new(1, 2).class).should.start_with?('#') end end @@ -209,7 +226,7 @@ describe "C-API Class function" do end it "returns a string for an anonymous class" do - @s.rb_class_name(Class.new).should be_kind_of(String) + @s.rb_class_name(Class.new).should.is_a?(String) end end @@ -223,22 +240,22 @@ describe "C-API Class function" do describe "rb_cvar_defined" do it "returns false when the class variable is not defined" do - @s.rb_cvar_defined(CApiClassSpecs::CVars, "@@nocvar").should be_false + @s.rb_cvar_defined(CApiClassSpecs::CVars, "@@nocvar").should == false end it "returns true when the class variable is defined" do - @s.rb_cvar_defined(CApiClassSpecs::CVars, "@@cvar").should be_true + @s.rb_cvar_defined(CApiClassSpecs::CVars, "@@cvar").should == true end it "returns true if the class instance variable is defined" do - @s.rb_cvar_defined(CApiClassSpecs::CVars, "@c_ivar").should be_true + @s.rb_cvar_defined(CApiClassSpecs::CVars, "@c_ivar").should == true end end describe "rb_cv_set" do it "sets a class variable" do o = CApiClassSpecs::CVars.new - o.new_cv.should be_nil + o.new_cv.should == nil @s.rb_cv_set(CApiClassSpecs::CVars, "@@new_cv", 1) o.new_cv.should == 1 CApiClassSpecs::CVars.remove_class_variable :@@new_cv @@ -253,14 +270,14 @@ describe "C-API Class function" do it "raises a NameError if the class variable is not defined" do -> { @s.rb_cv_get(CApiClassSpecs::CVars, "@@no_cvar") - }.should raise_error(NameError, /class variable @@no_cvar/) + }.should.raise(NameError, /class variable @@no_cvar/) end end describe "rb_cvar_set" do it "sets a class variable" do o = CApiClassSpecs::CVars.new - o.new_cvar.should be_nil + o.new_cvar.should == nil @s.rb_cvar_set(CApiClassSpecs::CVars, "@@new_cvar", 1) o.new_cvar.should == 1 CApiClassSpecs::CVars.remove_class_variable :@@new_cvar @@ -274,8 +291,8 @@ describe "C-API Class function" do end it "creates a subclass of the superclass" do - @cls.should be_kind_of(Class) - ClassSpecDefineClass.should equal(@cls) + @cls.should.is_a?(Class) + ClassSpecDefineClass.should.equal?(@cls) @cls.superclass.should == CApiClassSpecs::Super end @@ -292,19 +309,28 @@ describe "C-API Class function" do it "raises a TypeError when given a non class object to superclass" do -> { @s.rb_define_class("ClassSpecDefineClass3", Module.new) - }.should raise_error(TypeError) + }.should.raise(TypeError) end it "raises a TypeError when given a mismatched class to superclass" do -> { @s.rb_define_class("ClassSpecDefineClass", Object) - }.should raise_error(TypeError) + }.should.raise(TypeError) end it "raises a ArgumentError when given NULL as superclass" do -> { @s.rb_define_class("ClassSpecDefineClass4", nil) - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) + end + + it "allows arbitrary names, including constant names not valid in Ruby" do + cls = @s.rb_define_class("_INVALID_CLASS", CApiClassSpecs::Super) + cls.name.should == "_INVALID_CLASS" + + -> { + Object.const_get(cls.name) + }.should.raise(NameError, /wrong constant name/) end end @@ -313,8 +339,8 @@ describe "C-API Class function" do cls = @s.rb_define_class_under(CApiClassSpecs, "ClassUnder1", CApiClassSpecs::Super) - cls.should be_kind_of(Class) - CApiClassSpecs::Super.should be_ancestor_of(CApiClassSpecs::ClassUnder1) + cls.should.is_a?(Class) + CApiClassSpecs::ClassUnder1.ancestors.should.include?(CApiClassSpecs::Super) end it "sets the class name" do @@ -332,7 +358,7 @@ describe "C-API Class function" do -> { @s.rb_define_class_under(CApiClassSpecs, "ClassUnder5", Module.new) - }.should raise_error(TypeError) + }.should.raise(TypeError) end it "raises a TypeError when given a mismatched class to superclass" do @@ -340,7 +366,9 @@ describe "C-API Class function" do -> { @s.rb_define_class_under(CApiClassSpecs, "ClassUnder6", Class.new) - }.should raise_error(TypeError) + }.should.raise(TypeError) + ensure + CApiClassSpecs.send(:remove_const, :ClassUnder6) end it "defines a class for an existing Autoload" do @@ -348,15 +376,24 @@ describe "C-API Class function" do end it "raises a TypeError if class is defined and its superclass mismatches the given one" do - -> { @s.rb_define_class_under(CApiClassSpecs, "Sub", Object) }.should raise_error(TypeError) + -> { @s.rb_define_class_under(CApiClassSpecs, "Sub", Object) }.should.raise(TypeError) + end + + it "allows arbitrary names, including constant names not valid in Ruby" do + cls = @s.rb_define_class_under(CApiClassSpecs, "_INVALID_CLASS", CApiClassSpecs::Super) + cls.name.should == "CApiClassSpecs::_INVALID_CLASS" + + -> { + CApiClassSpecs.const_get(cls.name) + }.should.raise(NameError, /wrong constant name/) end end describe "rb_define_class_id_under" do it "creates a subclass of the superclass contained in a module" do cls = @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder1, CApiClassSpecs::Super) - cls.should be_kind_of(Class) - CApiClassSpecs::Super.should be_ancestor_of(CApiClassSpecs::ClassIdUnder1) + cls.should.is_a?(Class) + CApiClassSpecs::ClassIdUnder1.ancestors.should.include?(CApiClassSpecs::Super) end it "sets the class name" do @@ -375,14 +412,23 @@ describe "C-API Class function" do end it "raises a TypeError if class is defined and its superclass mismatches the given one" do - -> { @s.rb_define_class_id_under(CApiClassSpecs, :Sub, Object) }.should raise_error(TypeError) + -> { @s.rb_define_class_id_under(CApiClassSpecs, :Sub, Object) }.should.raise(TypeError) + end + + it "allows arbitrary names, including constant names not valid in Ruby" do + cls = @s.rb_define_class_id_under(CApiClassSpecs, :_INVALID_CLASS2, CApiClassSpecs::Super) + cls.name.should == "CApiClassSpecs::_INVALID_CLASS2" + + -> { + CApiClassSpecs.const_get(cls.name) + }.should.raise(NameError, /wrong constant name/) end end describe "rb_define_class_variable" do it "sets a class variable" do o = CApiClassSpecs::CVars.new - o.rbdcv_cvar.should be_nil + o.rbdcv_cvar.should == nil @s.rb_define_class_variable(CApiClassSpecs::CVars, "@@rbdcv_cvar", 1) o.rbdcv_cvar.should == 1 CApiClassSpecs::CVars.remove_class_variable :@@rbdcv_cvar @@ -397,23 +443,23 @@ describe "C-API Class function" do it "raises a NameError if the class variable is not defined" do -> { @s.rb_cvar_get(CApiClassSpecs::CVars, "@@no_cvar") - }.should raise_error(NameError, /class variable @@no_cvar/) + }.should.raise(NameError, /class variable @@no_cvar/) end end describe "rb_class_new" do it "returns a new subclass of the superclass" do subclass = @s.rb_class_new(CApiClassSpecs::NewClass) - CApiClassSpecs::NewClass.should be_ancestor_of(subclass) + subclass.ancestors.should.include?(CApiClassSpecs::NewClass) end it "raises a TypeError if passed Class as the superclass" do - -> { @s.rb_class_new(Class) }.should raise_error(TypeError) + -> { @s.rb_class_new(Class) }.should.raise(TypeError) end it "raises a TypeError if passed a singleton class as the superclass" do metaclass = Object.new.singleton_class - -> { @s.rb_class_new(metaclass) }.should raise_error(TypeError) + -> { @s.rb_class_new(metaclass) }.should.raise(TypeError) end end @@ -424,7 +470,7 @@ describe "C-API Class function" do end it "returns nil if the class has no superclass" do - @s.rb_class_superclass(BasicObject).should be_nil + @s.rb_class_superclass(BasicObject).should == nil end end @@ -445,4 +491,16 @@ describe "C-API Class function" do @s.rb_class_real(0).should == 0 end end + + describe "rb_class_get_superclass" do + it "returns parent class for a provided class" do + a = Class.new + @s.rb_class_get_superclass(Class.new(a)).should == a + end + + it "returns false when there is no parent class" do + @s.rb_class_get_superclass(BasicObject).should == false + @s.rb_class_get_superclass(Module.new).should == false + end + end end diff --git a/spec/ruby/optional/capi/constants_spec.rb b/spec/ruby/optional/capi/constants_spec.rb index 172d10a788..74584690ad 100644 --- a/spec/ruby/optional/capi/constants_spec.rb +++ b/spec/ruby/optional/capi/constants_spec.rb @@ -199,7 +199,7 @@ describe "C-API exception constant" do specify "rb_eFatal references the fatal class" do fatal = @s.rb_eFatal - fatal.should be_kind_of(Class) + fatal.should.is_a?(Class) fatal.should < Exception end diff --git a/spec/ruby/optional/capi/data_spec.rb b/spec/ruby/optional/capi/data_spec.rb index c03b863678..af1da10f48 100644 --- a/spec/ruby/optional/capi/data_spec.rb +++ b/spec/ruby/optional/capi/data_spec.rb @@ -1,45 +1,53 @@ require_relative 'spec_helper' +ruby_version_is ""..."3.4" do + load_extension("data") -load_extension("data") - -describe "CApiAllocSpecs (a class with an alloc func defined)" do - it "calls the alloc func" do - @s = CApiAllocSpecs.new - @s.wrapped_data.should == 42 # not defined in initialize - end -end - -describe "CApiWrappedStruct" do - before :each do - @s = CApiWrappedStructSpecs.new + describe "CApiAllocSpecs (a class with an alloc func defined)" do + it "calls the alloc func" do + @s = CApiAllocSpecs.new + @s.wrapped_data.should == 42 # not defined in initialize + end end - it "wraps with Data_Wrap_Struct and Data_Get_Struct returns data" do - a = @s.wrap_struct(1024) - @s.get_struct(a).should == 1024 - end + describe "CApiWrappedStruct" do + before :each do + @s = CApiWrappedStructSpecs.new + end - describe "RDATA()" do - it "returns the struct data" do + it "wraps with Data_Wrap_Struct and Data_Get_Struct returns data" do a = @s.wrap_struct(1024) - @s.get_struct_rdata(a).should == 1024 + @s.get_struct(a).should == 1024 end - it "allows changing the wrapped struct" do - a = @s.wrap_struct(1024) - @s.change_struct(a, 100) - @s.get_struct(a).should == 100 + describe "RDATA()" do + it "returns the struct data" do + a = @s.wrap_struct(1024) + @s.get_struct_rdata(a).should == 1024 + end + + it "allows changing the wrapped struct" do + a = @s.wrap_struct(1024) + @s.change_struct(a, 100) + @s.get_struct(a).should == 100 + end + + it "raises a TypeError if the object does not wrap a struct" do + -> { @s.get_struct(Object.new) }.should.raise(TypeError) + end end - it "raises a TypeError if the object does not wrap a struct" do - -> { @s.get_struct(Object.new) }.should raise_error(TypeError) + describe "rb_check_type" do + it "does not raise an exception when checking data objects" do + a = @s.wrap_struct(1024) + @s.rb_check_type(a, a).should == true + end end - end - describe "DATA_PTR" do - it "returns the struct data" do - a = @s.wrap_struct(1024) - @s.get_struct_data_ptr(a).should == 1024 + describe "DATA_PTR" do + it "returns the struct data" do + a = @s.wrap_struct(1024) + @s.get_struct_data_ptr(a).should == 1024 + end end end end diff --git a/spec/ruby/optional/capi/debug_spec.rb b/spec/ruby/optional/capi/debug_spec.rb new file mode 100644 index 0000000000..547b24cfcc --- /dev/null +++ b/spec/ruby/optional/capi/debug_spec.rb @@ -0,0 +1,74 @@ +require_relative 'spec_helper' + +load_extension('debug') + +describe "C-API Debug function" do + before :each do + @o = CApiDebugSpecs.new + end + + describe "rb_debug_inspector_open" do + it "creates a debug context and calls the given callback" do + @o.rb_debug_inspector_open(42).should.is_a?(Array) + @o.debug_spec_callback_data.should == 42 + end + end + + describe "rb_debug_inspector_frame_self_get" do + it "returns self" do + @o.rb_debug_inspector_frame_self_get(0).should == @o + @o.rb_debug_inspector_frame_self_get(1).should == self + end + end + + describe "rb_debug_inspector_frame_class_get" do + it "returns the frame class" do + @o.rb_debug_inspector_frame_class_get(0).should == CApiDebugSpecs + end + end + + describe "rb_debug_inspector_frame_binding_get" do + it "returns the current binding" do + a = "test" + b = @o.rb_debug_inspector_frame_binding_get(1) + b.should.instance_of?(Binding) + b.local_variable_get(:a).should == "test" + end + + it "matches the locations in rb_debug_inspector_backtrace_locations" do + frames = @o.rb_debug_inspector_open(42) + frames.each do |_s, klass, binding, iseq, backtrace_location| + if binding + # YJIT modifies Array#each backtraces but leaves its source_location as is + unless defined?(RubyVM::YJIT) && klass == Array && iseq.label == "each" + binding.source_location.should == [backtrace_location.path, backtrace_location.lineno] + end + method_name = binding.eval('__method__') + if method_name + method_name.should == backtrace_location.base_label.to_sym + end + end + end + end + end + + describe "rb_debug_inspector_frame_iseq_get" do + it "returns an InstructionSequence" do + if defined?(RubyVM::InstructionSequence) + @o.rb_debug_inspector_frame_iseq_get(1).should.instance_of?(RubyVM::InstructionSequence) + else + @o.rb_debug_inspector_frame_iseq_get(1).should == nil + end + end + end + + describe "rb_debug_inspector_backtrace_locations" do + it "returns an array of Thread::Backtrace::Location" do + bts = @o.rb_debug_inspector_backtrace_locations + bts.should_not.empty? + bts.each { |bt| bt.should.is_a?(Thread::Backtrace::Location) } + location = "#{__FILE__}:#{__LINE__ - 3}" + bts[1].to_s.should.include?(location) + end + end +end diff --git a/spec/ruby/optional/capi/digest_spec.rb b/spec/ruby/optional/capi/digest_spec.rb new file mode 100644 index 0000000000..65c5ecebb1 --- /dev/null +++ b/spec/ruby/optional/capi/digest_spec.rb @@ -0,0 +1,103 @@ +require_relative 'spec_helper' + +begin + require 'fiddle' +rescue LoadError + return +end + +load_extension('digest') + +describe "C-API Digest functions" do + before :each do + @s = CApiDigestSpecs.new + end + + describe "rb_digest_make_metadata" do + before :each do + @metadata = @s.rb_digest_make_metadata + end + + it "should store the block length" do + @s.block_length(@metadata).should == 40 + end + + it "should store the digest length" do + @s.digest_length(@metadata).should == 20 + end + + it "should store the context size" do + @s.context_size(@metadata).should == 129 + end + end + + describe "digest plugin" do + before :each do + @s = CApiDigestSpecs.new + @digest = Digest::TestDigest.new + + # A pointer to the CTX type defined in the extension for this spec. Digest does not make the context directly + # accessible as part of its API. However, to ensure we are properly loading the plugin, it's useful to have + # direct access to the context pointer to verify its contents. + @context = Fiddle::Pointer.new(@s.context(@digest)) + end + + it "should report the block length" do + @digest.block_length.should == 40 + end + + it "should report the digest length" do + @digest.digest_length.should == 20 + end + + it "should initialize the context" do + # Our test plugin always writes the string "Initialized\n" when its init function is called. + verify_context("Initialized\n") + end + + it "should update the digest" do + @digest.update("hello world") + + # Our test plugin always writes the string "Updated: <data>\n" when its update function is called. + current = "Initialized\nUpdated: hello world" + verify_context(current) + + @digest << "blah" + + current = "Initialized\nUpdated: hello worldUpdated: blah" + verify_context(current) + end + + it "should finalize the digest" do + @digest.update("") + + finish_string = @digest.instance_eval { finish } + + # We expect the plugin to write out the last `@digest.digest_length` bytes, followed by the string "Finished\n". + # + finish_string.should == "d\nUpdated: Finished\n" + finish_string.encoding.should == Encoding::ASCII_8BIT + end + + it "should reset the context" do + @digest.update("foo") + verify_context("Initialized\nUpdated: foo") + + @digest.reset + + # The context will be recreated as a result of the `reset` so we must fetch the latest context pointer. + @context = Fiddle::Pointer.new(@s.context(@digest)) + + verify_context("Initialized\n") + end + + def verify_context(current_body) + # In the CTX type, the length of the current context contents is stored in the first byte. + byte_count = @context[0] + byte_count.should == current_body.bytesize + + # After the size byte follows a string. + @context[1, byte_count].should == current_body + end + end +end diff --git a/spec/ruby/optional/capi/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb index 93bde54069..b77a967b1e 100644 --- a/spec/ruby/optional/capi/encoding_spec.rb +++ b/spec/ruby/optional/capi/encoding_spec.rb @@ -1,8 +1,9 @@ # -*- encoding: utf-8 -*- +# frozen_string_literal: false require_relative 'spec_helper' require_relative 'fixtures/encoding' -load_extension('encoding') +extension_path = load_extension('encoding') describe :rb_enc_get_index, shared: true do it "returns the index of the encoding of a String" do @@ -31,13 +32,11 @@ describe :rb_enc_set_index, shared: true do result.first.should == result.last end - ruby_version_is "2.6" do - it "raises an ArgumentError for a non-encoding capable object" do - obj = Object.new - -> { - result = @s.send(@method, obj, 1) - }.should raise_error(ArgumentError, "cannot set encoding on non-encoding capable object") - end + it "raises an ArgumentError for a non-encoding capable object" do + obj = Object.new + -> { + result = @s.send(@method, obj, 1) + }.should.raise(ArgumentError, "cannot set encoding on non-encoding capable object") end end @@ -48,13 +47,11 @@ describe "C-API Encoding function" do @s = CApiEncodingSpecs.new end - ruby_version_is "2.6" do - describe "rb_enc_alias" do - it "creates an alias for an existing Encoding" do - name = "ZOMGWTFBBQ#{@n += 1}" - @s.rb_enc_alias(name, "UTF-8").should >= 0 - Encoding.find(name).name.should == "UTF-8" - end + describe "rb_enc_alias" do + it "creates an alias for an existing Encoding" do + name = "ZOMGWTFBBQ#{@n += 1}" + @s.rb_enc_alias(name, "UTF-8").should >= 0 + Encoding.find(name).name.should == "UTF-8" end end @@ -67,6 +64,48 @@ describe "C-API Encoding function" do end end + describe "rb_enc_strlen" do + before :each do + @str = 'こにちわ' # Each codepoint in this string is 3 bytes in UTF-8 + end + + it "returns the correct string length for the encoding" do + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_8).should == 4 + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::BINARY).should == 12 + end + + it "returns the string length based on a fixed-width encoding's character length, even if the encoding is incompatible" do + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_16BE).should == 6 + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_16LE).should == 6 + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_32BE).should == 3 + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_32LE).should == 3 + end + + it "does not consider strings to be NUL-terminated" do + s = "abc\0def" + @s.rb_enc_strlen(s, s.bytesize, Encoding::US_ASCII).should == 7 + @s.rb_enc_strlen(s, s.bytesize, Encoding::UTF_8).should == 7 + end + + describe "handles broken strings" do + it "combines valid character and invalid character counts in UTF-8" do + # The result is 3 because `rb_enc_strlen` counts the first valid character and then adds + # the byte count for the invalid character that follows for 1 + 2. + @s.rb_enc_strlen(@str, 5, Encoding::UTF_8).should == 3 + end + + it "combines valid character and invalid character counts in UTF-16" do + @s.rb_enc_strlen(@str, 5, Encoding::UTF_16BE).should == 3 + end + + it "rounds up for fixed-width encodings" do + @s.rb_enc_strlen(@str, 7, Encoding::UTF_32BE).should == 2 + @s.rb_enc_strlen(@str, 7, Encoding::UTF_32LE).should == 2 + @s.rb_enc_strlen(@str, 5, Encoding::BINARY).should == 5 + end + end + end + describe "rb_enc_find" do it "returns the encoding of an Encoding" do @s.rb_enc_find("UTF-8").should == "UTF-8" @@ -126,16 +165,22 @@ describe "C-API Encoding function" do describe "rb_enc_from_index" do it "returns an Encoding" do - @s.rb_enc_from_index(0).should be_an_instance_of(String) + @s.rb_enc_from_index(0).should.instance_of?(String) end end describe "rb_enc_mbc_to_codepoint" do it "returns the correct codepoint for the given character and size" do - @s.rb_enc_mbc_to_codepoint("é", 2).should == 0x00E9 - @s.rb_enc_mbc_to_codepoint("éa", 2).should == 0x00E9 - @s.rb_enc_mbc_to_codepoint("éa", 1).should == 0xC3 - @s.rb_enc_mbc_to_codepoint("éa", 3).should == 0x00E9 + @s.rb_enc_mbc_to_codepoint("é").should == 0xE9 + end + + it "returns 0 if p == e" do + @s.rb_enc_mbc_to_codepoint("").should == 0 + end + + it "returns the raw byte if incomplete character in UTF-8" do + @s.rb_enc_mbc_to_codepoint("\xC3").should == 0xC3 + @s.rb_enc_mbc_to_codepoint("\x80").should == 0x80 end end @@ -239,11 +284,9 @@ describe "C-API Encoding function" do @s.rb_enc_get_index(1).should == -1 end - ruby_version_is "2.6" do - it "returns -1 for an object without an encoding" do - obj = Object.new - @s.rb_enc_get_index(obj).should == -1 - end + it "returns -1 for an object without an encoding" do + obj = Object.new + @s.rb_enc_get_index(obj).should == -1 end end @@ -255,7 +298,7 @@ describe "C-API Encoding function" do it "returns a String in US-ASCII encoding when high bits are set" do xEE = [0xEE].pack('C').force_encoding('utf-8') result = @s.rb_enc_str_new(xEE, 1, Encoding::US_ASCII) - result.encoding.should equal(Encoding::US_ASCII) + result.encoding.should.equal?(Encoding::US_ASCII) end end @@ -346,12 +389,12 @@ describe "C-API Encoding function" do it "returns true if the object encoding is only ASCII" do str = "abc".force_encoding("us-ascii") str.valid_encoding? # make sure to set the coderange - @s.ENC_CODERANGE_ASCIIONLY(str).should be_true + @s.ENC_CODERANGE_ASCIIONLY(str).should == true end it "returns false if the object encoding is not ASCII only" do str = "ありがとう".force_encoding("utf-8") - @s.ENC_CODERANGE_ASCIIONLY(str).should be_false + @s.ENC_CODERANGE_ASCIIONLY(str).should == false end end @@ -378,7 +421,7 @@ describe "C-API Encoding function" do describe "when the rb_encoding struct is stored in native memory" do it "can still read the name of the encoding" do address = @s.rb_to_encoding_native_store(Encoding::UTF_8) - address.should be_kind_of(Integer) + address.should.is_a?(Integer) @s.rb_to_encoding_native_name(address).should == "UTF-8" end end @@ -409,7 +452,7 @@ describe "C-API Encoding function" do describe "rb_enc_compatible" do it "returns 0 if the encodings of the Strings are not compatible" do a = [0xff].pack('C').force_encoding "binary" - b = "\u3042".encode("utf-8") + b = "あ" @s.rb_enc_compatible(a, b).should == 0 end @@ -418,11 +461,25 @@ describe "C-API Encoding function" do # Encoding.compatible? it "returns the same value as Encoding.compatible? if the Strings have a compatible encoding" do a = "abc".force_encoding("us-ascii") - b = "\u3042".encode("utf-8") + b = "あ" @s.rb_enc_compatible(a, b).should == Encoding.compatible?(a, b) end end + describe "rb_enc_check" do + it "returns the compatible encoding of the two Strings" do + a = "abc".force_encoding("us-ascii") + b = "あ" + @s.rb_enc_check(a, b).should == Encoding::UTF_8 + end + + it "raises Encoding::CompatibilityError if the encodings are not compatible" do + a = [0xff].pack('C').b + b = "あ" + -> { @s.rb_enc_check(a, b) }.should.raise(Encoding::CompatibilityError) + end + end + describe "rb_enc_copy" do before :each do @obj = "rb_enc_copy".encode(Encoding::US_ASCII) @@ -432,12 +489,20 @@ describe "C-API Encoding function" do @s.rb_enc_copy("string", @obj).encoding.should == Encoding::US_ASCII end - it "raises a RuntimeError if the second argument is a Symbol" do - -> { @s.rb_enc_copy(:symbol, @obj) }.should raise_error(RuntimeError) + it "raises a RuntimeError if the first argument is a Symbol" do + -> { @s.rb_enc_copy(:symbol, @obj) }.should.raise(RuntimeError) + end + + ruby_version_is "4.1" do + it "raises a FrozenError if the first argument is a Regexp" do + -> { @s.rb_enc_copy(/regexp/.dup, @obj) }.should.raise(FrozenError) + end end - it "sets the encoding of a Regexp to that of the second argument" do - @s.rb_enc_copy(/regexp/.dup, @obj).encoding.should == Encoding::US_ASCII + ruby_version_is ""..."4.1" do + it "sets the encoding of a Regexp to that of the second argument" do + @s.rb_enc_copy(/regexp/.dup, @obj).encoding.should == Encoding::US_ASCII + end end end @@ -452,7 +517,7 @@ describe "C-API Encoding function" do it "returns 0 if Encoding.default_internal is nil" do Encoding.default_internal = nil - @s.rb_default_internal_encoding.should be_nil + @s.rb_default_internal_encoding.should == nil end it "returns the encoding for Encoding.default_internal" do @@ -484,11 +549,19 @@ describe "C-API Encoding function" do end it "raises a RuntimeError if the argument is Symbol" do - -> { @s.rb_enc_associate(:symbol, "US-ASCII") }.should raise_error(RuntimeError) + -> { @s.rb_enc_associate(:symbol, "US-ASCII") }.should.raise(RuntimeError) end - it "sets the encoding of a Regexp to the encoding" do - @s.rb_enc_associate(/regexp/.dup, "BINARY").encoding.should == Encoding::BINARY + ruby_version_is "4.1" do + it "raises a FrozenError if the argument is a Regexp" do + -> { @s.rb_enc_associate(/regexp/.dup, "BINARY") }.should.raise(FrozenError) + end + end + + ruby_version_is ""..."4.1" do + it "sets the encoding of a Regexp to the encoding" do + @s.rb_enc_associate(/regexp/.dup, "BINARY").encoding.should == Encoding::BINARY + end end it "sets the encoding of a String to a default when the encoding is NULL" do @@ -503,33 +576,42 @@ describe "C-API Encoding function" do enc.should == Encoding::BINARY end - it "sets the encoding of a Regexp to the encoding" do - index = @s.rb_enc_find_index("UTF-8") - enc = @s.rb_enc_associate_index(/regexp/.dup, index).encoding - enc.should == Encoding::UTF_8 + ruby_version_is "4.1" do + it "raises a FrozenError if the argument is a Regexp" do + index = @s.rb_enc_find_index("UTF-8") + -> { @s.rb_enc_associate_index(/regexp/.dup, index) }.should.raise(FrozenError) + end + end + + ruby_version_is ""..."4.1" do + it "sets the encoding of a Regexp to the encoding" do + index = @s.rb_enc_find_index("UTF-8") + enc = @s.rb_enc_associate_index(/regexp/.dup, index).encoding + enc.should == Encoding::UTF_8 + end end it "sets the encoding of a Symbol to the encoding" do index = @s.rb_enc_find_index("UTF-8") - -> { @s.rb_enc_associate_index(:symbol, index) }.should raise_error(RuntimeError) + -> { @s.rb_enc_associate_index(:symbol, index) }.should.raise(RuntimeError) end end describe "rb_ascii8bit_encindex" do it "returns an index for the ASCII-8BIT encoding" do - @s.rb_ascii8bit_encindex().should >= 0 + @s.rb_ascii8bit_encindex().should == 0 end end describe "rb_utf8_encindex" do it "returns an index for the UTF-8 encoding" do - @s.rb_utf8_encindex().should >= 0 + @s.rb_utf8_encindex().should == 1 end end describe "rb_usascii_encindex" do it "returns an index for the US-ASCII encoding" do - @s.rb_usascii_encindex().should >= 0 + @s.rb_usascii_encindex().should == 2 end end @@ -567,13 +649,13 @@ describe "C-API Encoding function" do it "raises ArgumentError if an empty string is given" do -> do @s.rb_enc_codepoint_len("") - end.should raise_error(ArgumentError) + end.should.raise(ArgumentError) end it "raises ArgumentError if an invalid byte sequence is given" do -> do @s.rb_enc_codepoint_len([0xa0, 0xa1].pack('CC').force_encoding('utf-8')) # Invalid sequence identifier - end.should raise_error(ArgumentError) + end.should.raise(ArgumentError) end it "returns codepoint 0x24 and length 1 for character '$'" do @@ -607,11 +689,25 @@ describe "C-API Encoding function" do describe "rb_enc_str_asciionly_p" do it "returns true for an ASCII string" do - @s.rb_enc_str_asciionly_p("hello").should be_true + @s.rb_enc_str_asciionly_p("hello").should == true end it "returns false for a non-ASCII string" do - @s.rb_enc_str_asciionly_p("hüllo").should be_false + @s.rb_enc_str_asciionly_p("hüllo").should == false + end + end + + describe "rb_enc_raise" do + it "forces exception message encoding to the specified one" do + utf_8_incompatible_string = "\x81".b + + -> { + @s.rb_enc_raise(Encoding::UTF_8, RuntimeError, utf_8_incompatible_string) + }.should.raise { |e| + e.message.encoding.should == Encoding::UTF_8 + e.message.valid_encoding?.should == false + e.message.bytes.should == utf_8_incompatible_string.bytes + } end end @@ -619,23 +715,40 @@ describe "C-API Encoding function" do it 'converts a Unicode codepoint to a UTF-8 C string' do str = ' ' * 6 { - 0 => "\x01", - 0x7f => "\xC2\x80", - 0x7ff => "\xE0\xA0\x80", - 0xffff => "\xF0\x90\x80\x80", - 0x1fffff => "\xF8\x88\x80\x80\x80", - 0x3ffffff => "\xFC\x84\x80\x80\x80\x80", + 1 => "\x01", + 0x80 => "\xC2\x80", + 0x800 => "\xE0\xA0\x80", + 0x10000 => "\xF0\x90\x80\x80", + 0x200000 => "\xF8\x88\x80\x80\x80", + 0x4000000 => "\xFC\x84\x80\x80\x80\x80", }.each do |num, result| - len = @s.rb_uv_to_utf8(str, num + 1) - str[0..len-1].should == result + len = @s.rb_uv_to_utf8(str, num) + str.byteslice(0, len).should == result end end end + describe "rb_enc_left_char_head" do + it 'returns the head position of a character' do + @s.rb_enc_left_char_head("é", 1).should == 0 + @s.rb_enc_left_char_head("éééé", 7).should == 6 + + @s.rb_enc_left_char_head("a", 0).should == 0 + + # unclear if this is intended to work + @s.rb_enc_left_char_head("a", 1).should == 1 + + # Works because for single-byte encodings rb_enc_left_char_head() just returns the pointer + @s.rb_enc_left_char_head("a".force_encoding(Encoding::US_ASCII), 88).should == 88 + @s.rb_enc_left_char_head("a".b, 88).should == 88 + end + end + describe "ONIGENC_MBC_CASE_FOLD" do it "returns the correct case fold for the given string" do @s.ONIGENC_MBC_CASE_FOLD("lower").should == ["l", 1] @s.ONIGENC_MBC_CASE_FOLD("Upper").should == ["u", 1] + @s.ONIGENC_MBC_CASE_FOLD("ABC"[1..-1]).should == ["b", 1] end it "works with other encodings" do @@ -648,4 +761,57 @@ describe "C-API Encoding function" do str.bytes.should == [0, 0x24] end end + + describe "rb_define_dummy_encoding" do + run = 0 + + it "defines the dummy encoding" do + @s.rb_define_dummy_encoding("FOO#{run += 1}") + enc = Encoding.find("FOO#{run}") + enc.should.dummy? + end + + it "returns the index of the dummy encoding" do + index = @s.rb_define_dummy_encoding("BAR#{run += 1}") + index.should == Encoding.list.size - 1 + end + + it "raises EncodingError if too many encodings" do + code = <<-RUBY + require #{extension_path.dump} + 1_000.times {|i| CApiEncodingSpecs.new.rb_define_dummy_encoding("R_\#{i}") } + RUBY + ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)') + end + end + + describe "ONIGENC_IS_UNICODE" do + it "is true only for select UTF-related encodings" do + unicode = [ + Encoding::UTF_8, + Encoding::UTF8_DOCOMO, + Encoding::UTF8_KDDI, + Encoding::UTF8_MAC, + Encoding::UTF8_SOFTBANK, + Encoding::CESU_8, + Encoding::UTF_16LE, + Encoding::UTF_16BE, + Encoding::UTF_32LE, + Encoding::UTF_32BE + ] + unicode.each do |enc| + @s.should.ONIGENC_IS_UNICODE(enc) + end + + (Encoding.list - unicode).each { |enc| + @s.should_not.ONIGENC_IS_UNICODE(enc) + } + end + + # Redundant with the above but more explicit + it "is false for the dummy UTF-16 and UTF-32 encodings" do + @s.should_not.ONIGENC_IS_UNICODE(Encoding::UTF_16) + @s.should_not.ONIGENC_IS_UNICODE(Encoding::UTF_32) + end + end end diff --git a/spec/ruby/optional/capi/exception_spec.rb b/spec/ruby/optional/capi/exception_spec.rb index 9e82e5c7dd..429da093d1 100644 --- a/spec/ruby/optional/capi/exception_spec.rb +++ b/spec/ruby/optional/capi/exception_spec.rb @@ -28,13 +28,57 @@ describe "C-API Exception function" do describe "rb_exc_raise" do it "raises passed exception" do runtime_error = RuntimeError.new '42' - -> { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42') + -> { @s.rb_exc_raise(runtime_error) }.should.raise(RuntimeError, '42') end it "raises an exception with an empty backtrace" do runtime_error = RuntimeError.new '42' runtime_error.set_backtrace [] - -> { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42') + -> { @s.rb_exc_raise(runtime_error) }.should.raise(RuntimeError, '42') + end + + it "sets $! to the raised exception when not rescuing from an another exception" do + runtime_error = RuntimeError.new '42' + runtime_error.set_backtrace [] + begin + @s.rb_exc_raise(runtime_error) + rescue + $!.should == runtime_error + end + end + + it "sets $! to the raised exception when $! when rescuing from an another exception" do + runtime_error = RuntimeError.new '42' + runtime_error.set_backtrace [] + begin + begin + raise StandardError + rescue + @s.rb_exc_raise(runtime_error) + end + rescue + $!.should == runtime_error + end + end + end + + describe "rb_errinfo" do + it "is cleared when entering a C method" do + begin + raise StandardError + rescue + $!.class.should == StandardError + @s.rb_errinfo().should == nil + end + end + + it "does not clear $! in the calling method" do + begin + raise StandardError + rescue + @s.rb_errinfo() + $!.class.should == StandardError + end end end @@ -44,15 +88,69 @@ describe "C-API Exception function" do end it "accepts nil" do - @s.rb_set_errinfo(nil).should be_nil + @s.rb_set_errinfo(nil).should == nil end it "accepts an Exception instance" do - @s.rb_set_errinfo(Exception.new).should be_nil + @s.rb_set_errinfo(Exception.new).should == nil end it "raises a TypeError if the object is not nil or an Exception instance" do - -> { @s.rb_set_errinfo("error") }.should raise_error(TypeError) + -> { @s.rb_set_errinfo("error") }.should.raise(TypeError) + end + end + + describe "rb_error_frozen_object" do + it "raises a FrozenError regardless of the object's frozen state" do + # The type of the argument we supply doesn't matter. The choice here is arbitrary and we only change the type + # of the argument to ensure the exception messages are set correctly. + -> { @s.rb_error_frozen_object(Array.new) }.should.raise(FrozenError, "can't modify frozen Array: []") + -> { @s.rb_error_frozen_object(Array.new.freeze) }.should.raise(FrozenError, "can't modify frozen Array: []") + end + + it "properly handles recursive rb_error_frozen_object calls" do + klass = Class.new(Object) + object = klass.new + s = @s + klass.define_method :inspect do + s.rb_error_frozen_object(object) + end + + -> { @s.rb_error_frozen_object(object) }.should.raise(FrozenError, "can't modify frozen #{klass}: ...") + end + end + + describe "rb_syserr_new" do + it "returns system error with default message when passed message is NULL" do + exception = @s.rb_syserr_new(Errno::ENOENT::Errno, nil) + exception.class.should == Errno::ENOENT + exception.message.should.include?("No such file or directory") + exception.should.is_a?(SystemCallError) + end + + it "returns system error with custom message" do + exception = @s.rb_syserr_new(Errno::ENOENT::Errno, "custom message") + + exception.message.should.include?("custom message") + exception.class.should == Errno::ENOENT + exception.should.is_a?(SystemCallError) + end + end + + describe "rb_syserr_new_str" do + it "returns system error with default message when passed message is nil" do + exception = @s.rb_syserr_new_str(Errno::ENOENT::Errno, nil) + + exception.message.should.include?("No such file or directory") + exception.class.should == Errno::ENOENT + exception.should.is_a?(SystemCallError) + end + + it "returns system error with custom message" do + exception = @s.rb_syserr_new_str(Errno::ENOENT::Errno, "custom message") + exception.message.should.include?("custom message") + exception.class.should == Errno::ENOENT + exception.should.is_a?(SystemCallError) end end @@ -83,17 +181,17 @@ describe "C-API Exception function" do end it "raises a TypeError for incorrect types" do - -> { @s.rb_make_exception([nil]) }.should raise_error(TypeError) - -> { @s.rb_make_exception([Object.new]) }.should raise_error(TypeError) + -> { @s.rb_make_exception([nil]) }.should.raise(TypeError) + -> { @s.rb_make_exception([Object.new]) }.should.raise(TypeError) obj = Object.new def obj.exception "not exception type" end - -> { @s.rb_make_exception([obj]) }.should raise_error(TypeError) + -> { @s.rb_make_exception([obj]) }.should.raise(TypeError) end it "raises an ArgumentError for too many arguments" do - -> { @s.rb_make_exception([Exception, "Message", ["backtrace 1"], "extra"]) }.should raise_error(ArgumentError) + -> { @s.rb_make_exception([Exception, "Message", ["backtrace 1"], "extra"]) }.should.raise(ArgumentError) end it "returns nil for empty arguments" do diff --git a/spec/ruby/optional/capi/ext/array_spec.c b/spec/ruby/optional/capi/ext/array_spec.c index 39ed1ed18b..628c4df9d7 100644 --- a/spec/ruby/optional/capi/ext/array_spec.c +++ b/spec/ruby/optional/capi/ext/array_spec.c @@ -196,6 +196,7 @@ static VALUE copy_ary(RB_BLOCK_CALL_FUNC_ARGLIST(el, new_ary)) { return rb_ary_push(new_ary, el); } +#ifndef RUBY_VERSION_IS_4_0 static VALUE array_spec_rb_iterate(VALUE self, VALUE ary) { VALUE new_ary = rb_ary_new(); @@ -203,11 +204,21 @@ static VALUE array_spec_rb_iterate(VALUE self, VALUE ary) { return new_ary; } +#endif + +static VALUE array_spec_rb_block_call(VALUE self, VALUE ary) { + VALUE new_ary = rb_ary_new(); + + rb_block_call(ary, rb_intern("each"), 0, 0, copy_ary, new_ary); + + return new_ary; +} static VALUE sub_pair(RB_BLOCK_CALL_FUNC_ARGLIST(el, holder)) { return rb_ary_push(holder, rb_ary_entry(el, 1)); } +#ifndef RUBY_VERSION_IS_4_0 static VALUE each_pair(VALUE obj) { return rb_funcall(obj, rb_intern("each_pair"), 0); } @@ -219,16 +230,32 @@ static VALUE array_spec_rb_iterate_each_pair(VALUE self, VALUE obj) { return new_ary; } +#endif + +static VALUE array_spec_rb_block_call_each_pair(VALUE self, VALUE obj) { + VALUE new_ary = rb_ary_new(); + + rb_block_call(obj, rb_intern("each_pair"), 0, 0, sub_pair, new_ary); + + return new_ary; +} static VALUE iter_yield(RB_BLOCK_CALL_FUNC_ARGLIST(el, ary)) { rb_yield(el); return Qnil; } +#ifndef RUBY_VERSION_IS_4_0 static VALUE array_spec_rb_iterate_then_yield(VALUE self, VALUE obj) { rb_iterate(rb_each, obj, iter_yield, obj); return Qnil; } +#endif + +static VALUE array_spec_rb_block_call_then_yield(VALUE self, VALUE obj) { + rb_block_call(obj, rb_intern("each"), 0, 0, iter_yield, obj); + return Qnil; +} static VALUE array_spec_rb_mem_clear(VALUE self, VALUE obj) { VALUE ary[1]; @@ -287,9 +314,14 @@ void Init_array_spec(void) { rb_define_method(cls, "rb_ary_plus", array_spec_rb_ary_plus, 2); rb_define_method(cls, "rb_ary_unshift", array_spec_rb_ary_unshift, 2); rb_define_method(cls, "rb_assoc_new", array_spec_rb_assoc_new, 2); +#ifndef RUBY_VERSION_IS_4_0 rb_define_method(cls, "rb_iterate", array_spec_rb_iterate, 1); rb_define_method(cls, "rb_iterate_each_pair", array_spec_rb_iterate_each_pair, 1); rb_define_method(cls, "rb_iterate_then_yield", array_spec_rb_iterate_then_yield, 1); +#endif + rb_define_method(cls, "rb_block_call", array_spec_rb_block_call, 1); + rb_define_method(cls, "rb_block_call_each_pair", array_spec_rb_block_call_each_pair, 1); + rb_define_method(cls, "rb_block_call_then_yield", array_spec_rb_block_call_then_yield, 1); rb_define_method(cls, "rb_mem_clear", array_spec_rb_mem_clear, 1); rb_define_method(cls, "rb_ary_freeze", array_spec_rb_ary_freeze, 1); rb_define_method(cls, "rb_ary_to_ary", array_spec_rb_ary_to_ary, 1); diff --git a/spec/ruby/optional/capi/ext/class_spec.c b/spec/ruby/optional/capi/ext/class_spec.c index 36b8c8f2f3..8ac0e7a93f 100644 --- a/spec/ruby/optional/capi/ext/class_spec.c +++ b/spec/ruby/optional/capi/ext/class_spec.c @@ -61,27 +61,26 @@ static VALUE class_spec_rb_class_new(VALUE self, VALUE super) { return rb_class_new(super); } -static VALUE class_spec_rb_class_new_instance(VALUE self, - VALUE nargs, VALUE args, - VALUE klass) { - int c_nargs = FIX2INT(nargs); - VALUE *c_args = (VALUE*)alloca(sizeof(VALUE) * c_nargs); - int i; - - for (i = 0; i < c_nargs; i++) - c_args[i] = rb_ary_entry(args, i); +static VALUE class_spec_rb_class_new_instance(VALUE self, VALUE args, VALUE klass) { + return rb_class_new_instance(RARRAY_LENINT(args), RARRAY_PTR(args), klass); +} - return rb_class_new_instance(c_nargs, c_args, klass); +static VALUE class_spec_rb_class_new_instance_kw(VALUE self, VALUE args, VALUE klass) { + return rb_class_new_instance_kw(RARRAY_LENINT(args), RARRAY_PTR(args), klass, RB_PASS_KEYWORDS); } static VALUE class_spec_rb_class_real(VALUE self, VALUE object) { - if(rb_type_p(object, T_FIXNUM)) { + if (rb_type_p(object, T_FIXNUM)) { return INT2FIX(rb_class_real(FIX2INT(object))); } else { return rb_class_real(CLASS_OF(object)); } } +static VALUE class_spec_rb_class_get_superclass(VALUE self, VALUE klass) { + return rb_class_get_superclass(klass); +} + static VALUE class_spec_rb_class_superclass(VALUE self, VALUE klass) { return rb_class_superclass(klass); } @@ -119,19 +118,19 @@ VALUE class_spec_define_attr(VALUE self, VALUE klass, VALUE sym, VALUE read, VAL } static VALUE class_spec_rb_define_class(VALUE self, VALUE name, VALUE super) { - if(NIL_P(super)) super = 0; + if (NIL_P(super)) super = 0; return rb_define_class(RSTRING_PTR(name), super); } static VALUE class_spec_rb_define_class_under(VALUE self, VALUE outer, VALUE name, VALUE super) { - if(NIL_P(super)) super = 0; + if (NIL_P(super)) super = 0; return rb_define_class_under(outer, RSTRING_PTR(name), super); } static VALUE class_spec_rb_define_class_id_under(VALUE self, VALUE outer, VALUE name, VALUE super) { - if(NIL_P(super)) super = 0; + if (NIL_P(super)) super = 0; return rb_define_class_id_under(outer, SYM2ID(name), super); } @@ -145,19 +144,6 @@ static VALUE class_spec_include_module(VALUE self, VALUE klass, VALUE module) { return klass; } -static VALUE class_spec_method_var_args_1(int argc, VALUE *argv, VALUE self) { - VALUE ary = rb_ary_new(); - int i; - for (i = 0; i < argc; i++) { - rb_ary_push(ary, argv[i]); - } - return ary; -} - -static VALUE class_spec_method_var_args_2(VALUE self, VALUE argv) { - return argv; -} - void Init_class_spec(void) { VALUE cls = rb_define_class("CApiClassSpecs", rb_cObject); rb_define_method(cls, "define_call_super_method", class_spec_define_call_super_method, 2); @@ -171,8 +157,10 @@ void Init_class_spec(void) { rb_define_method(cls, "rb_class_protected_instance_methods", class_spec_rb_class_protected_instance_methods, -1); rb_define_method(cls, "rb_class_private_instance_methods", class_spec_rb_class_private_instance_methods, -1); rb_define_method(cls, "rb_class_new", class_spec_rb_class_new, 1); - rb_define_method(cls, "rb_class_new_instance", class_spec_rb_class_new_instance, 3); + rb_define_method(cls, "rb_class_new_instance", class_spec_rb_class_new_instance, 2); + rb_define_method(cls, "rb_class_new_instance_kw", class_spec_rb_class_new_instance_kw, 2); rb_define_method(cls, "rb_class_real", class_spec_rb_class_real, 1); + rb_define_method(cls, "rb_class_get_superclass", class_spec_rb_class_get_superclass, 1); rb_define_method(cls, "rb_class_superclass", class_spec_rb_class_superclass, 1); rb_define_method(cls, "rb_cvar_defined", class_spec_cvar_defined, 2); rb_define_method(cls, "rb_cvar_get", class_spec_cvar_get, 2); @@ -185,8 +173,6 @@ void Init_class_spec(void) { rb_define_method(cls, "rb_define_class_id_under", class_spec_rb_define_class_id_under, 3); rb_define_method(cls, "rb_define_class_variable", class_spec_define_class_variable, 3); rb_define_method(cls, "rb_include_module", class_spec_include_module, 2); - rb_define_method(cls, "rb_method_varargs_1", class_spec_method_var_args_1, -1); - rb_define_method(cls, "rb_method_varargs_2", class_spec_method_var_args_2, -2); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/constants_spec.c b/spec/ruby/optional/capi/ext/constants_spec.c index 9aee8db37f..05819ea476 100644 --- a/spec/ruby/optional/capi/ext/constants_spec.c +++ b/spec/ruby/optional/capi/ext/constants_spec.c @@ -14,9 +14,6 @@ defconstfunc(rb_cBinding) defconstfunc(rb_cClass) defconstfunc(rb_cComplex) defconstfunc(rb_mComparable) -#ifndef RUBY_VERSION_IS_3_0 -defconstfunc(rb_cData) -#endif defconstfunc(rb_cDir) defconstfunc(rb_cEncoding) defconstfunc(rb_mEnumerable) @@ -97,9 +94,6 @@ void Init_constants_spec(void) { rb_define_method(cls, "rb_cClass", constants_spec_rb_cClass, 0); rb_define_method(cls, "rb_cComplex", constants_spec_rb_cComplex, 0); rb_define_method(cls, "rb_mComparable", constants_spec_rb_mComparable, 0); - #ifndef RUBY_VERSION_IS_3_0 - rb_define_method(cls, "rb_cData", constants_spec_rb_cData, 0); - #endif rb_define_method(cls, "rb_cDir", constants_spec_rb_cDir, 0); rb_define_method(cls, "rb_cEncoding", constants_spec_rb_cEncoding, 0); rb_define_method(cls, "rb_mEnumerable", constants_spec_rb_mEnumerable, 0); diff --git a/spec/ruby/optional/capi/ext/data_spec.c b/spec/ruby/optional/capi/ext/data_spec.c index 109bded1d1..efefe37c3a 100644 --- a/spec/ruby/optional/capi/ext/data_spec.c +++ b/spec/ruby/optional/capi/ext/data_spec.c @@ -3,6 +3,7 @@ #include <string.h> +#ifndef RUBY_VERSION_IS_3_4 #ifdef __cplusplus extern "C" { #endif @@ -66,7 +67,14 @@ VALUE sws_change_struct(VALUE self, VALUE obj, VALUE new_val) { return Qnil; } +VALUE sws_rb_check_type(VALUE self, VALUE obj, VALUE other) { + rb_check_type(obj, TYPE(other)); + return Qtrue; +} +#endif + void Init_data_spec(void) { +#ifndef RUBY_VERSION_IS_3_4 VALUE cls = rb_define_class("CApiAllocSpecs", rb_cObject); rb_define_alloc_func(cls, sdaf_alloc_func); rb_define_method(cls, "wrapped_data", sdaf_get_struct, 0); @@ -76,6 +84,8 @@ void Init_data_spec(void) { rb_define_method(cls, "get_struct_rdata", sws_get_struct_rdata, 1); rb_define_method(cls, "get_struct_data_ptr", sws_get_struct_data_ptr, 1); rb_define_method(cls, "change_struct", sws_change_struct, 2); + rb_define_method(cls, "rb_check_type", sws_rb_check_type, 2); +#endif } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/debug_spec.c b/spec/ruby/optional/capi/ext/debug_spec.c new file mode 100644 index 0000000000..9131eda78b --- /dev/null +++ b/spec/ruby/optional/capi/ext/debug_spec.c @@ -0,0 +1,93 @@ +#include "ruby.h" +#include "rubyspec.h" +#include "ruby/debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static VALUE callback_data = Qfalse; + +static VALUE rb_debug_inspector_open_callback(const rb_debug_inspector_t *dc, void *ptr) { + if (!dc) { + rb_raise(rb_eRuntimeError, "rb_debug_inspector_t should not be NULL"); + } + + VALUE locations = rb_debug_inspector_backtrace_locations(dc); + int len = RARRAY_LENINT(locations); + VALUE results = rb_ary_new2(len); + for (int i = 0; i < len; i++) { + VALUE ary = rb_ary_new2(5); // [self, klass, binding, iseq, backtrace_location] + rb_ary_store(ary, 0, rb_debug_inspector_frame_self_get(dc, i)); + rb_ary_store(ary, 1, rb_debug_inspector_frame_class_get(dc, i)); + rb_ary_store(ary, 2, rb_debug_inspector_frame_binding_get(dc, i)); + rb_ary_store(ary, 3, rb_debug_inspector_frame_iseq_get(dc, i)); + rb_ary_store(ary, 4, rb_ary_entry(locations, i)); + rb_ary_push(results, ary); + } + callback_data = (VALUE)ptr; + return results; +} + +static VALUE rb_debug_inspector_frame_self_get_callback(const rb_debug_inspector_t *dc, void *ptr) { + return rb_debug_inspector_frame_self_get(dc, NUM2LONG((VALUE) ptr)); +} + +static VALUE rb_debug_inspector_frame_class_get_callback(const rb_debug_inspector_t *dc, void *ptr) { + return rb_debug_inspector_frame_class_get(dc, NUM2LONG((VALUE) ptr)); +} + +static VALUE rb_debug_inspector_frame_binding_get_callback(const rb_debug_inspector_t *dc, void *ptr) { + return rb_debug_inspector_frame_binding_get(dc, NUM2LONG((VALUE) ptr)); +} + +static VALUE rb_debug_inspector_frame_iseq_get_callback(const rb_debug_inspector_t *dc, void *ptr) { + return rb_debug_inspector_frame_iseq_get(dc, NUM2LONG((VALUE) ptr)); +} + +static VALUE debug_spec_callback_data(VALUE self) { + return callback_data; +} + +VALUE debug_spec_rb_debug_inspector_open(VALUE self, VALUE index) { + return rb_debug_inspector_open(rb_debug_inspector_open_callback, (void *)index); +} + +VALUE debug_spec_rb_debug_inspector_frame_self_get(VALUE self, VALUE index) { + return rb_debug_inspector_open(rb_debug_inspector_frame_self_get_callback, (void *)index); +} + +VALUE debug_spec_rb_debug_inspector_frame_class_get(VALUE self, VALUE index) { + return rb_debug_inspector_open(rb_debug_inspector_frame_class_get_callback, (void *)index); +} + +VALUE debug_spec_rb_debug_inspector_frame_binding_get(VALUE self, VALUE index) { + return rb_debug_inspector_open(rb_debug_inspector_frame_binding_get_callback, (void *)index); +} + +VALUE debug_spec_rb_debug_inspector_frame_iseq_get(VALUE self, VALUE index) { + return rb_debug_inspector_open(rb_debug_inspector_frame_iseq_get_callback, (void *)index); +} + +static VALUE rb_debug_inspector_backtrace_locations_func(const rb_debug_inspector_t *dc, void *ptr) { + return rb_debug_inspector_backtrace_locations(dc); +} + +VALUE debug_spec_rb_debug_inspector_backtrace_locations(VALUE self) { + return rb_debug_inspector_open(rb_debug_inspector_backtrace_locations_func, (void *)self); +} + +void Init_debug_spec(void) { + VALUE cls = rb_define_class("CApiDebugSpecs", rb_cObject); + rb_define_method(cls, "rb_debug_inspector_open", debug_spec_rb_debug_inspector_open, 1); + rb_define_method(cls, "rb_debug_inspector_frame_self_get", debug_spec_rb_debug_inspector_frame_self_get, 1); + rb_define_method(cls, "rb_debug_inspector_frame_class_get", debug_spec_rb_debug_inspector_frame_class_get, 1); + rb_define_method(cls, "rb_debug_inspector_frame_binding_get", debug_spec_rb_debug_inspector_frame_binding_get, 1); + rb_define_method(cls, "rb_debug_inspector_frame_iseq_get", debug_spec_rb_debug_inspector_frame_iseq_get, 1); + rb_define_method(cls, "rb_debug_inspector_backtrace_locations", debug_spec_rb_debug_inspector_backtrace_locations, 0); + rb_define_method(cls, "debug_spec_callback_data", debug_spec_callback_data, 0); +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/ruby/optional/capi/ext/digest_spec.c b/spec/ruby/optional/capi/ext/digest_spec.c new file mode 100644 index 0000000000..65c8defa20 --- /dev/null +++ b/spec/ruby/optional/capi/ext/digest_spec.c @@ -0,0 +1,168 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include "ruby/digest.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DIGEST_LENGTH 20 +#define BLOCK_LENGTH 40 + +const char *init_string = "Initialized\n"; +const char *update_string = "Updated: "; +const char *finish_string = "Finished\n"; + +#define PAYLOAD_SIZE 128 + +typedef struct CTX { + uint8_t pos; + char payload[PAYLOAD_SIZE]; +} CTX; + +void* context = NULL; + +int digest_spec_plugin_init(void *raw_ctx) { + // Make the context accessible to tests. This isn't safe, but there's no way to access the context otherwise. + context = raw_ctx; + + struct CTX *ctx = (struct CTX *)raw_ctx; + size_t len = strlen(init_string); + + // Clear the payload since this init function will be invoked as part of the `reset` operation. + memset(ctx->payload, 0, PAYLOAD_SIZE); + + // Write a simple value we can verify in tests. + // This is not what a real digest would do, but we're using a dummy digest plugin to test interactions. + memcpy(ctx->payload, init_string, len); + ctx->pos = (uint8_t) len; + + return 1; +} + +void digest_spec_plugin_update(void *raw_ctx, unsigned char *ptr, size_t size) { + struct CTX *ctx = (struct CTX *)raw_ctx; + size_t update_str_len = strlen(update_string); + + if (ctx->pos + update_str_len + size >= PAYLOAD_SIZE) { + rb_raise(rb_eRuntimeError, "update size too large; reset the digest and write fewer updates"); + } + + // Write the supplied value to the payload so it can be easily verified in test. + // This is not what a real digest would do, but we're using a dummy digest plugin to test interactions. + memcpy(ctx->payload + ctx->pos, update_string, update_str_len); + ctx->pos += update_str_len; + + memcpy(ctx->payload + ctx->pos, ptr, size); + ctx->pos += size; + + return; +} + +int digest_spec_plugin_finish(void *raw_ctx, unsigned char *ptr) { + struct CTX *ctx = (struct CTX *)raw_ctx; + size_t finish_string_len = strlen(finish_string); + + // We're always going to write DIGEST_LENGTH bytes. In a real plugin, this would be the digest value. Here we + // write out a text string in order to make validation in tests easier. + // + // In order to delineate the output more clearly from an `Digest#update` call, we always write out the + // `finish_string` message. That leaves `DIGEST_LENGTH - finish_string_len` bytes to read out of the context. + size_t context_bytes = DIGEST_LENGTH - finish_string_len; + + memcpy(ptr, ctx->payload + (ctx->pos - context_bytes), context_bytes); + memcpy(ptr + context_bytes, finish_string, finish_string_len); + + return 1; +} + +static const rb_digest_metadata_t metadata = { + // The RUBY_DIGEST_API_VERSION value comes from ruby/digest.h and may vary based on the Ruby being tested. Since + // it isn't publicly exposed in the digest gem, we ignore for these tests. Either the test hard-codes an expected + // value and is subject to breaking depending on the Ruby being run or we publicly expose `RUBY_DIGEST_API_VERSION`, + // in which case the test would pass trivially. + RUBY_DIGEST_API_VERSION, + DIGEST_LENGTH, + BLOCK_LENGTH, + sizeof(CTX), + (rb_digest_hash_init_func_t) digest_spec_plugin_init, + (rb_digest_hash_update_func_t) digest_spec_plugin_update, + (rb_digest_hash_finish_func_t) digest_spec_plugin_finish, +}; + +// The `get_metadata_ptr` function is not publicly available in the digest gem. However, we need to use +// to extract the `rb_digest_metadata_t*` value set up by the plugin so we reproduce and adjust the +// definition here. +// +// Taken and adapted from https://github.com/ruby/digest/blob/v3.2.0/ext/digest/digest.c#L558-L568 +static rb_digest_metadata_t * get_metadata_ptr(VALUE obj) { + rb_digest_metadata_t *algo; + +#ifdef DIGEST_USE_RB_EXT_RESOLVE_SYMBOL + // In the digest gem there is an additional data type check performed before reading the value out. + // Since the type definition isn't public, we can't use it as part of a type check here so we omit it. + // This is safe to do because this code is intended to only load digest plugins written as part of this test suite. + algo = (rb_digest_metadata_t *) RTYPEDDATA_DATA(obj); +#else +# undef RUBY_UNTYPED_DATA_WARNING +# define RUBY_UNTYPED_DATA_WARNING 0 + Data_Get_Struct(obj, rb_digest_metadata_t, algo); +#endif + + return algo; +} + +VALUE digest_spec_rb_digest_make_metadata(VALUE self) { + return rb_digest_make_metadata(&metadata); +} + +VALUE digest_spec_block_length(VALUE self, VALUE meta) { + rb_digest_metadata_t* algo = get_metadata_ptr(meta); + + return SIZET2NUM(algo->block_len); +} + +VALUE digest_spec_digest_length(VALUE self, VALUE meta) { + rb_digest_metadata_t* algo = get_metadata_ptr(meta); + + return SIZET2NUM(algo->digest_len); +} + +VALUE digest_spec_context_size(VALUE self, VALUE meta) { + rb_digest_metadata_t* algo = get_metadata_ptr(meta); + + return SIZET2NUM(algo->ctx_size); +} + +#ifndef PTR2NUM +#define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) +#endif + +VALUE digest_spec_context(VALUE self, VALUE digest) { + return PTR2NUM(context); +} + +void Init_digest_spec(void) { + VALUE cls; + + cls = rb_define_class("CApiDigestSpecs", rb_cObject); + rb_define_method(cls, "rb_digest_make_metadata", digest_spec_rb_digest_make_metadata, 0); + rb_define_method(cls, "block_length", digest_spec_block_length, 1); + rb_define_method(cls, "digest_length", digest_spec_digest_length, 1); + rb_define_method(cls, "context_size", digest_spec_context_size, 1); + rb_define_method(cls, "context", digest_spec_context, 1); + + VALUE mDigest, cDigest_Base, cDigest; + + mDigest = rb_define_module("Digest"); + mDigest = rb_digest_namespace(); + cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base")); + + cDigest = rb_define_class_under(mDigest, "TestDigest", cDigest_Base); + rb_iv_set(cDigest, "metadata", rb_digest_make_metadata(&metadata)); +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/ruby/optional/capi/ext/encoding_spec.c b/spec/ruby/optional/capi/ext/encoding_spec.c index cde4d0c351..2038a5d4a8 100644 --- a/spec/ruby/optional/capi/ext/encoding_spec.c +++ b/spec/ruby/optional/capi/ext/encoding_spec.c @@ -12,7 +12,7 @@ static VALUE encoding_spec_MBCLEN_CHARFOUND_P(VALUE self, VALUE obj) { } static VALUE encoding_spec_ENC_CODERANGE_ASCIIONLY(VALUE self, VALUE obj) { - if(ENC_CODERANGE_ASCIIONLY(obj)) { + if (ENC_CODERANGE_ASCIIONLY(obj)) { return Qtrue; } else { return Qfalse; @@ -61,21 +61,19 @@ static VALUE encoding_spec_rb_filesystem_encindex(VALUE self) { static VALUE encoding_spec_rb_default_internal_encoding(VALUE self) { rb_encoding* enc = rb_default_internal_encoding(); - if(enc == 0) return Qnil; + if (enc == 0) return Qnil; return rb_str_new2(enc->name); } static VALUE encoding_spec_rb_default_external_encoding(VALUE self) { rb_encoding* enc = rb_default_external_encoding(); - if(enc == 0) return Qnil; + if (enc == 0) return Qnil; return rb_str_new2(enc->name); } -#ifdef RUBY_VERSION_IS_2_6 static VALUE encoding_spec_rb_enc_alias(VALUE self, VALUE alias, VALUE orig) { return INT2NUM(rb_enc_alias(RSTRING_PTR(alias), RSTRING_PTR(orig))); } -#endif static VALUE encoding_spec_rb_enc_associate(VALUE self, VALUE obj, VALUE enc) { return rb_enc_associate(obj, NIL_P(enc) ? NULL : rb_enc_find(RSTRING_PTR(enc))); @@ -88,8 +86,13 @@ static VALUE encoding_spec_rb_enc_associate_index(VALUE self, VALUE obj, VALUE i static VALUE encoding_spec_rb_enc_compatible(VALUE self, VALUE a, VALUE b) { rb_encoding* enc = rb_enc_compatible(a, b); - if(!enc) return INT2FIX(0); + if (!enc) return INT2FIX(0); + + return rb_enc_from_encoding(enc); +} +static VALUE encoding_spec_rb_enc_check(VALUE self, VALUE a, VALUE b) { + rb_encoding* enc = rb_enc_check(a, b); return rb_enc_from_encoding(enc); } @@ -120,10 +123,9 @@ static VALUE encoding_spec_rb_enc_from_index(VALUE self, VALUE index) { return rb_str_new2(rb_enc_from_index(NUM2INT(index))->name); } -static VALUE encoding_spec_rb_enc_mbc_to_codepoint(VALUE self, VALUE str, VALUE offset) { - int o = FIX2INT(offset); +static VALUE encoding_spec_rb_enc_mbc_to_codepoint(VALUE self, VALUE str) { char *p = RSTRING_PTR(str); - char *e = p + o; + char *e = RSTRING_END(str); return INT2FIX(rb_enc_mbc_to_codepoint(p, e, rb_enc_get(str))); } @@ -274,8 +276,17 @@ static VALUE encoding_spec_rb_enc_str_asciionly_p(VALUE self, VALUE str) { } } +static VALUE encoding_spec_rb_enc_raise(VALUE self, VALUE encoding, VALUE exception_class, VALUE format) { + rb_encoding *e = rb_to_encoding(encoding); + const char *f = RSTRING_PTR(format); + + rb_enc_raise(e, exception_class, "%s", f); +} + static VALUE encoding_spec_rb_uv_to_utf8(VALUE self, VALUE buf, VALUE num) { - return INT2NUM(rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num))); + int len = rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num)); + RB_ENC_CODERANGE_CLEAR(buf); + return INT2NUM(len); } static VALUE encoding_spec_ONIGENC_MBC_CASE_FOLD(VALUE self, VALUE str) { @@ -300,6 +311,28 @@ static VALUE encoding_spec_rb_enc_codelen(VALUE self, VALUE code, VALUE encoding return INT2FIX(rb_enc_codelen(c, enc)); } +static VALUE encoding_spec_rb_enc_strlen(VALUE self, VALUE str, VALUE length, VALUE encoding) { + int l = FIX2INT(length); + char *p = RSTRING_PTR(str); + char *e = p + l; + + return LONG2FIX(rb_enc_strlen(p, e, rb_to_encoding(encoding))); +} + +static VALUE encoding_spec_rb_enc_left_char_head(VALUE self, VALUE str, VALUE offset) { + char *ptr = RSTRING_PTR(str); + char *result = rb_enc_left_char_head(ptr, ptr + NUM2INT(offset), RSTRING_END(str), rb_enc_get(str)); + return LONG2NUM(result - ptr); +} + +static VALUE encoding_spec_rb_define_dummy_encoding(VALUE self, VALUE name) { + return INT2NUM(rb_define_dummy_encoding(RSTRING_PTR(name))); +} + +static VALUE encoding_spec_ONIGENC_IS_UNICODE(VALUE self, VALUE encoding) { + return ONIGENC_IS_UNICODE(rb_to_encoding(encoding)) ? Qtrue : Qfalse; +} + void Init_encoding_spec(void) { VALUE cls; native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*)); @@ -318,28 +351,23 @@ void Init_encoding_spec(void) { rb_define_method(cls, "rb_locale_encindex", encoding_spec_rb_locale_encindex, 0); rb_define_method(cls, "rb_filesystem_encoding", encoding_spec_rb_filesystem_encoding, 0); rb_define_method(cls, "rb_filesystem_encindex", encoding_spec_rb_filesystem_encindex, 0); - rb_define_method(cls, "rb_default_internal_encoding", - encoding_spec_rb_default_internal_encoding, 0); - - rb_define_method(cls, "rb_default_external_encoding", - encoding_spec_rb_default_external_encoding, 0); - -#ifdef RUBY_VERSION_IS_2_6 + rb_define_method(cls, "rb_default_internal_encoding", encoding_spec_rb_default_internal_encoding, 0); + rb_define_method(cls, "rb_default_external_encoding", encoding_spec_rb_default_external_encoding, 0); rb_define_method(cls, "rb_enc_alias", encoding_spec_rb_enc_alias, 2); -#endif - rb_define_method(cls, "MBCLEN_CHARFOUND_P", encoding_spec_MBCLEN_CHARFOUND_P, 1); rb_define_method(cls, "rb_enc_associate", encoding_spec_rb_enc_associate, 2); rb_define_method(cls, "rb_enc_associate_index", encoding_spec_rb_enc_associate_index, 2); rb_define_method(cls, "rb_enc_compatible", encoding_spec_rb_enc_compatible, 2); + rb_define_method(cls, "rb_enc_check", encoding_spec_rb_enc_check, 2); rb_define_method(cls, "rb_enc_copy", encoding_spec_rb_enc_copy, 2); rb_define_method(cls, "rb_enc_codelen", encoding_spec_rb_enc_codelen, 2); + rb_define_method(cls, "rb_enc_strlen", encoding_spec_rb_enc_strlen, 3); rb_define_method(cls, "rb_enc_find", encoding_spec_rb_enc_find, 1); rb_define_method(cls, "rb_enc_find_index", encoding_spec_rb_enc_find_index, 1); rb_define_method(cls, "rb_enc_isalnum", encoding_spec_rb_enc_isalnum, 2); rb_define_method(cls, "rb_enc_isspace", encoding_spec_rb_enc_isspace, 2); rb_define_method(cls, "rb_enc_from_index", encoding_spec_rb_enc_from_index, 1); - rb_define_method(cls, "rb_enc_mbc_to_codepoint", encoding_spec_rb_enc_mbc_to_codepoint, 2); + rb_define_method(cls, "rb_enc_mbc_to_codepoint", encoding_spec_rb_enc_mbc_to_codepoint, 1); rb_define_method(cls, "rb_enc_mbcput", encoding_spec_rb_enc_mbcput, 2); rb_define_method(cls, "rb_enc_from_encoding", encoding_spec_rb_enc_from_encoding, 1); rb_define_method(cls, "rb_enc_get", encoding_spec_rb_enc_get, 1); @@ -361,8 +389,12 @@ void Init_encoding_spec(void) { rb_define_method(cls, "rb_enc_nth", encoding_spec_rb_enc_nth, 2); rb_define_method(cls, "rb_enc_codepoint_len", encoding_spec_rb_enc_codepoint_len, 1); rb_define_method(cls, "rb_enc_str_asciionly_p", encoding_spec_rb_enc_str_asciionly_p, 1); + rb_define_method(cls, "rb_enc_raise", encoding_spec_rb_enc_raise, 3); rb_define_method(cls, "rb_uv_to_utf8", encoding_spec_rb_uv_to_utf8, 2); rb_define_method(cls, "ONIGENC_MBC_CASE_FOLD", encoding_spec_ONIGENC_MBC_CASE_FOLD, 1); + rb_define_method(cls, "rb_enc_left_char_head", encoding_spec_rb_enc_left_char_head, 2); + rb_define_method(cls, "rb_define_dummy_encoding", encoding_spec_rb_define_dummy_encoding, 1); + rb_define_method(cls, "ONIGENC_IS_UNICODE", encoding_spec_ONIGENC_IS_UNICODE, 1); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/exception_spec.c b/spec/ruby/optional/capi/ext/exception_spec.c index 7250792b70..c3b94d7bcd 100644 --- a/spec/ruby/optional/capi/ext/exception_spec.c +++ b/spec/ruby/optional/capi/ext/exception_spec.c @@ -8,6 +8,10 @@ extern "C" { #endif +VALUE exception_spec_rb_errinfo(VALUE self) { + return rb_errinfo(); +} + VALUE exception_spec_rb_exc_new(VALUE self, VALUE str) { char *cstr = StringValuePtr(str); return rb_exc_new(rb_eException, cstr, strlen(cstr)); @@ -23,7 +27,7 @@ VALUE exception_spec_rb_exc_new3(VALUE self, VALUE str) { } VALUE exception_spec_rb_exc_raise(VALUE self, VALUE exc) { - if (self != Qundef) rb_exc_raise(exc); + if (self != Qundef) rb_exc_raise(exc); return Qnil; } @@ -32,6 +36,28 @@ VALUE exception_spec_rb_set_errinfo(VALUE self, VALUE exc) { return Qnil; } +NORETURN(VALUE exception_spec_rb_error_frozen_object(VALUE self, VALUE object)); + +VALUE exception_spec_rb_error_frozen_object(VALUE self, VALUE object) { + rb_error_frozen_object(object); + UNREACHABLE_RETURN(Qnil); +} + +VALUE exception_spec_rb_syserr_new(VALUE self, VALUE num, VALUE msg) { + int n = NUM2INT(num); + char *cstr = NULL; + + if (msg != Qnil) { + cstr = StringValuePtr(msg); + } + + return rb_syserr_new(n, cstr); +} + +VALUE exception_spec_rb_syserr_new_str(VALUE self, VALUE num, VALUE msg) { + int n = NUM2INT(num); + return rb_syserr_new_str(n, msg); +} VALUE exception_spec_rb_make_exception(VALUE self, VALUE ary) { int argc = RARRAY_LENINT(ary); @@ -41,11 +67,15 @@ VALUE exception_spec_rb_make_exception(VALUE self, VALUE ary) { void Init_exception_spec(void) { VALUE cls = rb_define_class("CApiExceptionSpecs", rb_cObject); + rb_define_method(cls, "rb_errinfo", exception_spec_rb_errinfo, 0); rb_define_method(cls, "rb_exc_new", exception_spec_rb_exc_new, 1); rb_define_method(cls, "rb_exc_new2", exception_spec_rb_exc_new2, 1); rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1); rb_define_method(cls, "rb_exc_raise", exception_spec_rb_exc_raise, 1); rb_define_method(cls, "rb_set_errinfo", exception_spec_rb_set_errinfo, 1); + rb_define_method(cls, "rb_error_frozen_object", exception_spec_rb_error_frozen_object, 1); + rb_define_method(cls, "rb_syserr_new", exception_spec_rb_syserr_new, 2); + rb_define_method(cls, "rb_syserr_new_str", exception_spec_rb_syserr_new_str, 2); rb_define_method(cls, "rb_make_exception", exception_spec_rb_make_exception, 1); } diff --git a/spec/ruby/optional/capi/ext/fiber_spec.c b/spec/ruby/optional/capi/ext/fiber_spec.c new file mode 100644 index 0000000000..db54f7ad8c --- /dev/null +++ b/spec/ruby/optional/capi/ext/fiber_spec.c @@ -0,0 +1,64 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +VALUE fiber_spec_rb_fiber_current(VALUE self) { + return rb_fiber_current(); +} + +VALUE fiber_spec_rb_fiber_alive_p(VALUE self, VALUE fiber) { + return rb_fiber_alive_p(fiber); +} + +VALUE fiber_spec_rb_fiber_resume(VALUE self, VALUE fiber, VALUE ary) { + long argc = RARRAY_LEN(ary); + VALUE *argv = (VALUE*) alloca(sizeof(VALUE) * argc); + int i; + + for (i = 0; i < argc; i++) { + argv[i] = rb_ary_entry(ary, i); + } + + return rb_fiber_resume(fiber, (int)argc, argv); +} + +VALUE fiber_spec_rb_fiber_yield(VALUE self, VALUE ary) { + long argc = RARRAY_LEN(ary); + VALUE *argv = (VALUE*) alloca(sizeof(VALUE) * argc); + int i; + + for (i = 0; i < argc; i++) { + argv[i] = rb_ary_entry(ary, i); + } + return rb_fiber_yield((int)argc, argv); +} + +VALUE fiber_spec_rb_fiber_new_function(RB_BLOCK_CALL_FUNC_ARGLIST(args, dummy)) { + return rb_funcall(args, rb_intern("inspect"), 0); +} + +VALUE fiber_spec_rb_fiber_new(VALUE self) { + return rb_fiber_new(fiber_spec_rb_fiber_new_function, Qnil); +} + +VALUE fiber_spec_rb_fiber_raise(int argc, VALUE *argv, VALUE self) { + VALUE fiber = argv[0]; + return rb_fiber_raise(fiber, argc-1, argv+1); +} + +void Init_fiber_spec(void) { + VALUE cls = rb_define_class("CApiFiberSpecs", rb_cObject); + rb_define_method(cls, "rb_fiber_current", fiber_spec_rb_fiber_current, 0); + rb_define_method(cls, "rb_fiber_alive_p", fiber_spec_rb_fiber_alive_p, 1); + rb_define_method(cls, "rb_fiber_resume", fiber_spec_rb_fiber_resume, 2); + rb_define_method(cls, "rb_fiber_yield", fiber_spec_rb_fiber_yield, 1); + rb_define_method(cls, "rb_fiber_new", fiber_spec_rb_fiber_new, 0); + rb_define_method(cls, "rb_fiber_raise", fiber_spec_rb_fiber_raise, -1); +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/ruby/optional/capi/ext/finalizer_spec.c b/spec/ruby/optional/capi/ext/finalizer_spec.c new file mode 100644 index 0000000000..83347da912 --- /dev/null +++ b/spec/ruby/optional/capi/ext/finalizer_spec.c @@ -0,0 +1,25 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static VALUE define_finalizer(VALUE self, VALUE obj, VALUE finalizer) { + return rb_define_finalizer(obj, finalizer); +} + +static VALUE undefine_finalizer(VALUE self, VALUE obj) { + return rb_undefine_finalizer(obj); +} + +void Init_finalizer_spec(void) { + VALUE cls = rb_define_class("CApiFinalizerSpecs", rb_cObject); + + rb_define_method(cls, "rb_define_finalizer", define_finalizer, 2); + rb_define_method(cls, "rb_undefine_finalizer", undefine_finalizer, 1); +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/ruby/optional/capi/ext/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c index 7dc9c347c7..2637ad27ac 100644 --- a/spec/ruby/optional/capi/ext/gc_spec.c +++ b/spec/ruby/optional/capi/ext/gc_spec.c @@ -7,6 +7,16 @@ extern "C" { VALUE registered_tagged_value; VALUE registered_reference_value; +VALUE registered_before_rb_gc_register_address; +VALUE registered_before_rb_global_variable_string; +VALUE registered_before_rb_global_variable_bignum; +VALUE registered_before_rb_global_variable_float; +VALUE registered_after_rb_global_variable_string; +VALUE registered_after_rb_global_variable_bignum; +VALUE registered_after_rb_global_variable_float; +VALUE rb_gc_register_address_outside_init; + +VALUE rb_gc_register_mark_object_not_referenced_float; static VALUE registered_tagged_address(VALUE self) { return registered_tagged_value; @@ -16,6 +26,45 @@ static VALUE registered_reference_address(VALUE self) { return registered_reference_value; } +static VALUE get_registered_before_rb_gc_register_address(VALUE self) { + return registered_before_rb_gc_register_address; +} + +static VALUE get_registered_before_rb_global_variable_string(VALUE self) { + return registered_before_rb_global_variable_string; +} + +static VALUE get_registered_before_rb_global_variable_bignum(VALUE self) { + return registered_before_rb_global_variable_bignum; +} + +static VALUE get_registered_before_rb_global_variable_float(VALUE self) { + return registered_before_rb_global_variable_float; +} + +static VALUE get_registered_after_rb_global_variable_string(VALUE self) { + return registered_after_rb_global_variable_string; +} + +static VALUE get_registered_after_rb_global_variable_bignum(VALUE self) { + return registered_after_rb_global_variable_bignum; +} + +static VALUE get_registered_after_rb_global_variable_float(VALUE self) { + return registered_after_rb_global_variable_float; +} + +static VALUE gc_spec_rb_gc_register_address(VALUE self) { + rb_gc_register_address(&rb_gc_register_address_outside_init); + rb_gc_register_address_outside_init = rb_str_new_cstr("rb_gc_register_address() outside Init_"); + return rb_gc_register_address_outside_init; +} + +static VALUE gc_spec_rb_gc_unregister_address(VALUE self) { + rb_gc_unregister_address(&rb_gc_register_address_outside_init); + return Qnil; +} + static VALUE gc_spec_rb_gc_enable(VALUE self) { return rb_gc_enable(); } @@ -29,7 +78,7 @@ static VALUE gc_spec_rb_gc(VALUE self) { return Qnil; } -static VALUE gc_spec_rb_gc_latest_gc_info(VALUE self, VALUE hash_or_key){ +static VALUE gc_spec_rb_gc_latest_gc_info(VALUE self, VALUE hash_or_key) { return rb_gc_latest_gc_info(hash_or_key); } @@ -43,21 +92,55 @@ static VALUE gc_spec_rb_gc_register_mark_object(VALUE self, VALUE obj) { return Qnil; } +static VALUE gc_spec_rb_gc_register_mark_object_not_referenced_float(VALUE self) { + return rb_gc_register_mark_object_not_referenced_float; +} + void Init_gc_spec(void) { VALUE cls = rb_define_class("CApiGCSpecs", rb_cObject); - registered_tagged_value = INT2NUM(10); - registered_reference_value = rb_str_new2("Globally registered data"); rb_gc_register_address(®istered_tagged_value); rb_gc_register_address(®istered_reference_value); + rb_gc_register_address(®istered_before_rb_gc_register_address); + rb_global_variable(®istered_before_rb_global_variable_string); + rb_global_variable(®istered_before_rb_global_variable_bignum); + rb_global_variable(®istered_before_rb_global_variable_float); + + registered_tagged_value = INT2NUM(10); + registered_reference_value = rb_str_new2("Globally registered data"); + registered_before_rb_gc_register_address = rb_str_new_cstr("registered before rb_gc_register_address()"); + + registered_before_rb_global_variable_string = rb_str_new_cstr("registered before rb_global_variable()"); + registered_before_rb_global_variable_bignum = LL2NUM(INT64_MAX); + registered_before_rb_global_variable_float = DBL2NUM(3.14); + + registered_after_rb_global_variable_string = rb_str_new_cstr("registered after rb_global_variable()"); + rb_global_variable(®istered_after_rb_global_variable_string); + registered_after_rb_global_variable_bignum = LL2NUM(INT64_MAX); + rb_global_variable(®istered_after_rb_global_variable_bignum); + registered_after_rb_global_variable_float = DBL2NUM(6.28); + rb_global_variable(®istered_after_rb_global_variable_float); + + rb_gc_register_mark_object_not_referenced_float = DBL2NUM(1.61); + rb_gc_register_mark_object(rb_gc_register_mark_object_not_referenced_float); rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0); rb_define_method(cls, "registered_reference_address", registered_reference_address, 0); + rb_define_method(cls, "registered_before_rb_gc_register_address", get_registered_before_rb_gc_register_address, 0); + rb_define_method(cls, "registered_before_rb_global_variable_string", get_registered_before_rb_global_variable_string, 0); + rb_define_method(cls, "registered_before_rb_global_variable_bignum", get_registered_before_rb_global_variable_bignum, 0); + rb_define_method(cls, "registered_before_rb_global_variable_float", get_registered_before_rb_global_variable_float, 0); + rb_define_method(cls, "registered_after_rb_global_variable_string", get_registered_after_rb_global_variable_string, 0); + rb_define_method(cls, "registered_after_rb_global_variable_bignum", get_registered_after_rb_global_variable_bignum, 0); + rb_define_method(cls, "registered_after_rb_global_variable_float", get_registered_after_rb_global_variable_float, 0); + rb_define_method(cls, "rb_gc_register_address", gc_spec_rb_gc_register_address, 0); + rb_define_method(cls, "rb_gc_unregister_address", gc_spec_rb_gc_unregister_address, 0); rb_define_method(cls, "rb_gc_enable", gc_spec_rb_gc_enable, 0); rb_define_method(cls, "rb_gc_disable", gc_spec_rb_gc_disable, 0); rb_define_method(cls, "rb_gc", gc_spec_rb_gc, 0); rb_define_method(cls, "rb_gc_adjust_memory_usage", gc_spec_rb_gc_adjust_memory_usage, 1); rb_define_method(cls, "rb_gc_register_mark_object", gc_spec_rb_gc_register_mark_object, 1); + rb_define_method(cls, "rb_gc_register_mark_object_not_referenced_float", gc_spec_rb_gc_register_mark_object_not_referenced_float, 0); rb_define_method(cls, "rb_gc_latest_gc_info", gc_spec_rb_gc_latest_gc_info, 1); } diff --git a/spec/ruby/optional/capi/ext/globals_spec.c b/spec/ruby/optional/capi/ext/globals_spec.c index 28a9633f98..20dea1a05a 100644 --- a/spec/ruby/optional/capi/ext/globals_spec.c +++ b/spec/ruby/optional/capi/ext/globals_spec.c @@ -20,6 +20,16 @@ static VALUE sb_define_hooked_variable(VALUE self, VALUE var_name) { return Qnil; } +static VALUE sb_define_hooked_variable_default_accessors(VALUE self, VALUE var_name) { + rb_define_hooked_variable(StringValuePtr(var_name), &g_hooked_var, (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL); + return Qnil; +} + +static VALUE sb_define_hooked_variable_null_var(VALUE self, VALUE var_name) { + rb_define_hooked_variable(StringValuePtr(var_name), NULL, (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL); + return Qnil; +} + VALUE g_ro_var; static VALUE sb_define_readonly_variable(VALUE self, VALUE var_name, VALUE val) { @@ -40,6 +50,26 @@ static VALUE sb_define_variable(VALUE self, VALUE var_name, VALUE val) { return Qnil; } +long virtual_var_storage; + +VALUE incrementing_getter(ID id, VALUE *data) { + return LONG2FIX(virtual_var_storage++); +} + +void incrementing_setter(VALUE val, ID id, VALUE *data) { + virtual_var_storage = FIX2LONG(val); +} + +static VALUE sb_define_virtual_variable_default_accessors(VALUE self, VALUE name) { + rb_define_virtual_variable(StringValuePtr(name), (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL); + return Qnil; +} + +static VALUE sb_define_virtual_variable_incrementing_accessors(VALUE self, VALUE name) { + rb_define_virtual_variable(StringValuePtr(name), incrementing_getter, incrementing_setter); + return Qnil; +} + static VALUE sb_f_global_variables(VALUE self) { return rb_f_global_variables(); } @@ -101,10 +131,14 @@ void Init_globals_spec(void) { VALUE cls = rb_define_class("CApiGlobalSpecs", rb_cObject); g_hooked_var = Qnil; rb_define_method(cls, "rb_define_hooked_variable_2x", sb_define_hooked_variable, 1); + rb_define_method(cls, "rb_define_hooked_variable_default_accessors", sb_define_hooked_variable_default_accessors, 1); + rb_define_method(cls, "rb_define_hooked_variable_null_var", sb_define_hooked_variable_null_var, 1); g_ro_var = Qnil; rb_define_method(cls, "rb_define_readonly_variable", sb_define_readonly_variable, 2); g_var = Qnil; rb_define_method(cls, "rb_define_variable", sb_define_variable, 2); + rb_define_method(cls, "rb_define_virtual_variable_default_accessors", sb_define_virtual_variable_default_accessors, 1); + rb_define_method(cls, "rb_define_virtual_variable_incrementing_accessors", sb_define_virtual_variable_incrementing_accessors, 1); rb_define_method(cls, "sb_get_global_value", sb_get_global_value, 0); rb_define_method(cls, "rb_f_global_variables", sb_f_global_variables, 0); rb_define_method(cls, "sb_gv_get", sb_gv_get, 1); diff --git a/spec/ruby/optional/capi/ext/hash_spec.c b/spec/ruby/optional/capi/ext/hash_spec.c index 7f38708915..653917f2c4 100644 --- a/spec/ruby/optional/capi/ext/hash_spec.c +++ b/spec/ruby/optional/capi/ext/hash_spec.c @@ -105,6 +105,10 @@ VALUE hash_spec_rb_hash_new(VALUE self) { return rb_hash_new(); } +VALUE hash_spec_rb_hash_new_capa(VALUE self, VALUE capacity) { + return rb_hash_new_capa(NUM2LONG(capacity)); +} + VALUE rb_ident_hash_new(void); /* internal.h, used in ripper */ VALUE hash_spec_rb_ident_hash_new(VALUE self) { @@ -128,6 +132,20 @@ VALUE hash_spec_compute_a_hash_code(VALUE self, VALUE seed) { return ULONG2NUM(h); } +VALUE hash_spec_rb_hash_bulk_insert(VALUE self, VALUE array_len, VALUE array, VALUE hash) { + VALUE* ptr; + + if (array == Qnil) { + ptr = NULL; + } else { + ptr = RARRAY_PTR(array); + } + + long len = FIX2LONG(array_len); + rb_hash_bulk_insert(len, ptr, hash); + return Qnil; +} + void Init_hash_spec(void) { VALUE cls = rb_define_class("CApiHashSpecs", rb_cObject); rb_define_method(cls, "rb_hash", hash_spec_rb_hash, 1); @@ -149,10 +167,12 @@ void Init_hash_spec(void) { rb_define_method(cls, "rb_hash_lookup2", hash_spec_rb_hash_lookup2, 3); rb_define_method(cls, "rb_hash_lookup2_default_undef", hash_spec_rb_hash_lookup2_default_undef, 2); rb_define_method(cls, "rb_hash_new", hash_spec_rb_hash_new, 0); + rb_define_method(cls, "rb_hash_new_capa", hash_spec_rb_hash_new_capa, 1); rb_define_method(cls, "rb_ident_hash_new", hash_spec_rb_ident_hash_new, 0); rb_define_method(cls, "rb_hash_size", hash_spec_rb_hash_size, 1); rb_define_method(cls, "rb_hash_set_ifnone", hash_spec_rb_hash_set_ifnone, 2); rb_define_method(cls, "compute_a_hash_code", hash_spec_compute_a_hash_code, 1); + rb_define_method(cls, "rb_hash_bulk_insert", hash_spec_rb_hash_bulk_insert, 3); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/integer_spec.c b/spec/ruby/optional/capi/ext/integer_spec.c index 5ed1d3fd73..792fc0652a 100644 --- a/spec/ruby/optional/capi/ext/integer_spec.c +++ b/spec/ruby/optional/capi/ext/integer_spec.c @@ -6,16 +6,15 @@ extern "C" { #endif static VALUE integer_spec_rb_integer_pack(VALUE self, VALUE value, - VALUE words, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags) -{ + VALUE words, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags) { int result = rb_integer_pack(value, (void*)RSTRING_PTR(words), FIX2INT(numwords), FIX2INT(wordsize), FIX2INT(nails), FIX2INT(flags)); return INT2FIX(result); } -VALUE rb_int_positive_pow(long x, unsigned long y); /* internal.h, used in ripper */ +RUBY_EXTERN VALUE rb_int_positive_pow(long x, unsigned long y); /* internal.h, used in ripper */ -static VALUE integer_spec_rb_int_positive_pow(VALUE self, VALUE a, VALUE b){ +static VALUE integer_spec_rb_int_positive_pow(VALUE self, VALUE a, VALUE b) { return rb_int_positive_pow(FIX2INT(a), FIX2INT(b)); } diff --git a/spec/ruby/optional/capi/ext/io_spec.c b/spec/ruby/optional/capi/ext/io_spec.c index 73e6804013..fe31cffb49 100644 --- a/spec/ruby/optional/capi/ext/io_spec.c +++ b/spec/ruby/optional/capi/ext/io_spec.c @@ -28,9 +28,7 @@ static int set_non_blocking(int fd) { } static int io_spec_get_fd(VALUE io) { - rb_io_t* fp; - GetOpenFile(io, fp); - return fp->fd; + return rb_io_descriptor(io); } VALUE io_spec_GetOpenFile_fd(VALUE self, VALUE io) { @@ -130,7 +128,7 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { rb_sys_fail("set_non_blocking failed"); #ifndef SET_NON_BLOCKING_FAILS_ALWAYS - if(RTEST(read_p)) { + if (RTEST(read_p)) { if (read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) { return Qnil; } @@ -139,9 +137,9 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { errno = saved_errno; } - ret = rb_io_wait_readable(fd); + ret = rb_io_maybe_wait_readable(errno, io, Qnil); - if(RTEST(read_p)) { + if (RTEST(read_p)) { ssize_t r = read(fd, buf, RB_IO_WAIT_READABLE_BUF); if (r != RB_IO_WAIT_READABLE_BUF) { perror("read"); @@ -153,28 +151,80 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { return ret ? Qtrue : Qfalse; #else - UNREACHABLE; + UNREACHABLE_RETURN(Qnil); #endif } VALUE io_spec_rb_io_wait_writable(VALUE self, VALUE io) { - int ret = rb_io_wait_writable(io_spec_get_fd(io)); + int ret = rb_io_maybe_wait_writable(errno, io, Qnil); return ret ? Qtrue : Qfalse; } +VALUE io_spec_rb_io_maybe_wait_writable(VALUE self, VALUE error, VALUE io, VALUE timeout) { + int ret = rb_io_maybe_wait_writable(NUM2INT(error), io, timeout); + return INT2NUM(ret); +} + +#ifdef SET_NON_BLOCKING_FAILS_ALWAYS +NORETURN(VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE timeout, VALUE read_p)); +#endif + +VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE timeout, VALUE read_p) { + int fd = io_spec_get_fd(io); +#ifndef SET_NON_BLOCKING_FAILS_ALWAYS + char buf[RB_IO_WAIT_READABLE_BUF]; + int ret, saved_errno; +#endif + + if (set_non_blocking(fd) == -1) + rb_sys_fail("set_non_blocking failed"); + +#ifndef SET_NON_BLOCKING_FAILS_ALWAYS + if (RTEST(read_p)) { + if (read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) { + return Qnil; + } + saved_errno = errno; + rb_ivar_set(self, rb_intern("@write_data"), Qtrue); + errno = saved_errno; + } + + // main part + ret = rb_io_maybe_wait_readable(NUM2INT(error), io, timeout); + + if (RTEST(read_p)) { + ssize_t r = read(fd, buf, RB_IO_WAIT_READABLE_BUF); + if (r != RB_IO_WAIT_READABLE_BUF) { + perror("read"); + return SSIZET2NUM(r); + } + rb_ivar_set(self, rb_intern("@read_data"), + rb_str_new(buf, RB_IO_WAIT_READABLE_BUF)); + } + + return INT2NUM(ret); +#else + UNREACHABLE_RETURN(Qnil); +#endif +} + +VALUE io_spec_rb_io_maybe_wait(VALUE self, VALUE error, VALUE io, VALUE events, VALUE timeout) { + return rb_io_maybe_wait(NUM2INT(error), io, events, timeout); +} + VALUE io_spec_rb_thread_wait_fd(VALUE self, VALUE io) { rb_thread_wait_fd(io_spec_get_fd(io)); return Qnil; } VALUE io_spec_rb_wait_for_single_fd(VALUE self, VALUE io, VALUE events, VALUE secs, VALUE usecs) { - int fd = io_spec_get_fd(io); - struct timeval tv; + VALUE timeout = Qnil; if (!NIL_P(secs)) { - tv.tv_sec = FIX2INT(secs); - tv.tv_usec = FIX2INT(usecs); + timeout = rb_float_new((double)FIX2INT(secs) + (0.000001 * FIX2INT(usecs))); } - return INT2FIX(rb_wait_for_single_fd(fd, FIX2INT(events), NIL_P(secs) ? NULL : &tv)); + VALUE result = rb_io_wait(io, events, timeout); + if (result == Qfalse) return INT2FIX(0); + else return result; } VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) { @@ -182,6 +232,46 @@ VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) { return Qnil; } +VALUE io_spec_rb_thread_fd_select_read(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, &fds, NULL, NULL, NULL); + rb_fd_term(&fds); + return INT2FIX(r); +} + +VALUE io_spec_rb_thread_fd_select_write(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, NULL, &fds, NULL, NULL); + rb_fd_term(&fds); + return INT2FIX(r); +} + +VALUE io_spec_rb_thread_fd_select_timeout(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + struct timeval timeout; + timeout.tv_sec = 10; + timeout.tv_usec = 20; + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, NULL, &fds, NULL, &timeout); + rb_fd_term(&fds); + return INT2FIX(r); +} + VALUE io_spec_rb_io_binmode(VALUE self, VALUE io) { return rb_io_binmode(io); } @@ -197,6 +287,18 @@ VALUE io_spec_rb_cloexec_open(VALUE self, VALUE path, VALUE flags, VALUE mode) { return rb_funcall(rb_cIO, rb_intern("for_fd"), 1, INT2FIX(fd)); } +VALUE io_spec_rb_cloexec_dup(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + int new_fd = rb_cloexec_dup(fd); + return rb_funcall(rb_cIO, rb_intern("for_fd"), 1, INT2FIX(new_fd)); +} + +VALUE io_spec_rb_cloexec_fcntl_dupfd(VALUE self, VALUE io, VALUE minfd) { + int fd = io_spec_get_fd(io); + int new_fd = rb_cloexec_fcntl_dupfd(fd, FIX2INT(minfd)); + return rb_funcall(rb_cIO, rb_intern("for_fd"), 1, INT2FIX(new_fd)); +} + VALUE io_spec_rb_io_close(VALUE self, VALUE io) { return rb_io_close(io); } @@ -209,7 +311,7 @@ VALUE io_spec_rb_io_set_nonblock(VALUE self, VALUE io) { GetOpenFile(io, fp); rb_io_set_nonblock(fp); #ifdef F_GETFL - flags = fcntl(fp->fd, F_GETFL, 0); + flags = fcntl(io_spec_get_fd(io), F_GETFL, 0); return flags & O_NONBLOCK ? Qtrue : Qfalse; #else return Qfalse; @@ -227,6 +329,43 @@ static VALUE io_spec_errno_set(VALUE self, VALUE val) { return val; } +VALUE io_spec_mode_sync_flag(VALUE self, VALUE io) { + int mode; + mode = rb_io_mode(io); + if (mode & FMODE_SYNC) { + return Qtrue; + } else { + return Qfalse; + } +} + +static VALUE io_spec_rb_io_mode(VALUE self, VALUE io) { + return INT2FIX(rb_io_mode(io)); +} + +static VALUE io_spec_rb_io_path(VALUE self, VALUE io) { + return rb_io_path(io); +} + +static VALUE io_spec_rb_io_closed_p(VALUE self, VALUE io) { + return rb_io_closed_p(io); +} + +static VALUE io_spec_rb_io_open_descriptor(VALUE self, VALUE klass, VALUE descriptor, VALUE mode, VALUE path, VALUE timeout, VALUE internal_encoding, VALUE external_encoding, VALUE ecflags, VALUE ecopts) { + struct rb_io_encoding io_encoding; + + io_encoding.enc = rb_to_encoding(internal_encoding); + io_encoding.enc2 = rb_to_encoding(external_encoding); + io_encoding.ecflags = FIX2INT(ecflags); + io_encoding.ecopts = ecopts; + + return rb_io_open_descriptor(klass, FIX2INT(descriptor), FIX2INT(mode), path, timeout, &io_encoding); +} + +static VALUE io_spec_rb_io_open_descriptor_without_encoding(VALUE self, VALUE klass, VALUE descriptor, VALUE mode, VALUE path, VALUE timeout) { + return rb_io_open_descriptor(klass, FIX2INT(descriptor), FIX2INT(mode), path, timeout, NULL); +} + void Init_io_spec(void) { VALUE cls = rb_define_class("CApiIOSpecs", rb_cObject); rb_define_method(cls, "GetOpenFile_fd", io_spec_GetOpenFile_fd, 1); @@ -244,13 +383,32 @@ void Init_io_spec(void) { rb_define_method(cls, "rb_io_taint_check", io_spec_rb_io_taint_check, 1); rb_define_method(cls, "rb_io_wait_readable", io_spec_rb_io_wait_readable, 2); rb_define_method(cls, "rb_io_wait_writable", io_spec_rb_io_wait_writable, 1); + rb_define_method(cls, "rb_io_maybe_wait_writable", io_spec_rb_io_maybe_wait_writable, 3); + rb_define_method(cls, "rb_io_maybe_wait_readable", io_spec_rb_io_maybe_wait_readable, 4); + rb_define_method(cls, "rb_io_maybe_wait", io_spec_rb_io_maybe_wait, 4); rb_define_method(cls, "rb_thread_wait_fd", io_spec_rb_thread_wait_fd, 1); rb_define_method(cls, "rb_thread_fd_writable", io_spec_rb_thread_fd_writable, 1); + rb_define_method(cls, "rb_thread_fd_select_read", io_spec_rb_thread_fd_select_read, 1); + rb_define_method(cls, "rb_thread_fd_select_write", io_spec_rb_thread_fd_select_write, 1); + rb_define_method(cls, "rb_thread_fd_select_timeout", io_spec_rb_thread_fd_select_timeout, 1); rb_define_method(cls, "rb_wait_for_single_fd", io_spec_rb_wait_for_single_fd, 4); rb_define_method(cls, "rb_io_binmode", io_spec_rb_io_binmode, 1); rb_define_method(cls, "rb_fd_fix_cloexec", io_spec_rb_fd_fix_cloexec, 1); rb_define_method(cls, "rb_cloexec_open", io_spec_rb_cloexec_open, 3); + rb_define_method(cls, "rb_cloexec_dup", io_spec_rb_cloexec_dup, 1); + rb_define_method(cls, "rb_cloexec_fcntl_dupfd", io_spec_rb_cloexec_fcntl_dupfd, 2); rb_define_method(cls, "errno=", io_spec_errno_set, 1); + rb_define_method(cls, "rb_io_mode_sync_flag", io_spec_mode_sync_flag, 1); + rb_define_method(cls, "rb_io_mode", io_spec_rb_io_mode, 1); + rb_define_method(cls, "rb_io_path", io_spec_rb_io_path, 1); + rb_define_method(cls, "rb_io_closed_p", io_spec_rb_io_closed_p, 1); + rb_define_method(cls, "rb_io_open_descriptor", io_spec_rb_io_open_descriptor, 9); + rb_define_method(cls, "rb_io_open_descriptor_without_encoding", io_spec_rb_io_open_descriptor_without_encoding, 5); + rb_define_const(cls, "FMODE_READABLE", INT2FIX(FMODE_READABLE)); + rb_define_const(cls, "FMODE_WRITABLE", INT2FIX(FMODE_WRITABLE)); + rb_define_const(cls, "FMODE_BINMODE", INT2FIX(FMODE_BINMODE)); + rb_define_const(cls, "FMODE_TEXTMODE", INT2FIX(FMODE_TEXTMODE)); + rb_define_const(cls, "ECONV_UNIVERSAL_NEWLINE_DECORATOR", INT2FIX(ECONV_UNIVERSAL_NEWLINE_DECORATOR)); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c index 4048684b2c..eee324052d 100644 --- a/spec/ruby/optional/capi/ext/kernel_spec.c +++ b/spec/ruby/optional/capi/ext/kernel_spec.c @@ -1,4 +1,5 @@ #include "ruby.h" +#include "ruby/vm.h" #include "rubyspec.h" #include <errno.h> @@ -7,16 +8,12 @@ extern "C" { #endif -VALUE kernel_spec_call_proc(VALUE arg_array) { +static VALUE kernel_spec_call_proc(VALUE arg_array) { VALUE arg = rb_ary_pop(arg_array); VALUE proc = rb_ary_pop(arg_array); return rb_funcall(proc, rb_intern("call"), 1, arg); } -VALUE kernel_spec_call_proc_raise(VALUE arg_array, VALUE raised_exc) { - return kernel_spec_call_proc(arg_array); -} - static VALUE kernel_spec_rb_block_given_p(VALUE self) { return rb_block_given_p() ? Qtrue : Qfalse; } @@ -71,11 +68,20 @@ VALUE kernel_spec_rb_block_call_no_func(VALUE self, VALUE ary) { return rb_block_call(ary, rb_intern("map"), 0, NULL, (rb_block_call_func_t)NULL, Qnil); } - VALUE kernel_spec_rb_frame_this_func(VALUE self) { return ID2SYM(rb_frame_this_func()); } +VALUE kernel_spec_rb_category_warn_deprecated(VALUE self) { + rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "foo"); + return Qnil; +} + +VALUE kernel_spec_rb_category_warn_deprecated_with_integer_extra_value(VALUE self, VALUE value) { + rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "foo %d", FIX2INT(value)); + return Qnil; +} + VALUE kernel_spec_rb_ensure(VALUE self, VALUE main_proc, VALUE arg, VALUE ensure_proc, VALUE arg2) { VALUE main_array, ensure_array; @@ -112,6 +118,12 @@ VALUE kernel_spec_rb_eval_string(VALUE self, VALUE str) { return rb_eval_string(RSTRING_PTR(str)); } +#ifndef RUBY_VERSION_IS_4_0 +VALUE kernel_spec_rb_eval_cmd_kw(VALUE self, VALUE cmd, VALUE args, VALUE kw_splat) { + return rb_eval_cmd_kw(cmd, args, NUM2INT(kw_splat)); +} +#endif + VALUE kernel_spec_rb_raise(VALUE self, VALUE hash) { rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("before"))); if (self != Qundef) @@ -130,7 +142,16 @@ VALUE kernel_spec_rb_throw_obj(VALUE self, VALUE obj, VALUE result) { return ID2SYM(rb_intern("rb_throw_failed")); } -VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) { +VALUE kernel_spec_rb_errinfo(VALUE self) { + return rb_errinfo(); +} + +VALUE kernel_spec_rb_set_errinfo(VALUE self, VALUE exc) { + rb_set_errinfo(exc); + return Qnil; +} + +static VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) { VALUE argv[2]; int argc; @@ -142,7 +163,7 @@ VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) { argc = 2; - return rb_funcall2(proc, rb_intern("call"), argc, argv); + return rb_funcallv(proc, rb_intern("call"), argc, argv); } VALUE kernel_spec_rb_rescue(VALUE self, VALUE main_proc, VALUE arg, @@ -177,7 +198,7 @@ VALUE kernel_spec_rb_rescue2(int argc, VALUE *args, VALUE self) { rb_ary_push(raise_array, args[3]); return rb_rescue2(kernel_spec_call_proc, main_array, - kernel_spec_call_proc_raise, raise_array, args[4], args[5], (VALUE)0); + kernel_spec_call_proc_with_raised_exc, raise_array, args[4], args[5], (VALUE)0); } static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) { @@ -191,6 +212,14 @@ static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) { return res; } +static VALUE kernel_spec_rb_protect_ignore_status(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) { return rb_protect(rb_yield, obj, NULL); } @@ -208,8 +237,8 @@ static VALUE kernel_spec_rb_eval_string_protect(VALUE self, VALUE str, VALUE ary VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) { errno = 1; - if(msg == Qnil) { - rb_sys_fail(0); + if (msg == Qnil) { + rb_sys_fail(NULL); } else if (self != Qundef) { rb_sys_fail(StringValuePtr(msg)); } @@ -217,7 +246,7 @@ VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) { } VALUE kernel_spec_rb_syserr_fail(VALUE self, VALUE err, VALUE msg) { - if(msg == Qnil) { + if (msg == Qnil) { rb_syserr_fail(NUM2INT(err), NULL); } else if (self != Qundef) { rb_syserr_fail(NUM2INT(err), StringValuePtr(msg)); @@ -225,6 +254,13 @@ VALUE kernel_spec_rb_syserr_fail(VALUE self, VALUE err, VALUE msg) { return Qnil; } +VALUE kernel_spec_rb_syserr_fail_str(VALUE self, VALUE err, VALUE msg) { + if (self != Qundef) { + rb_syserr_fail_str(NUM2INT(err), msg); + } + return Qnil; +} + VALUE kernel_spec_rb_warn(VALUE self, VALUE msg) { rb_warn("%s", StringValuePtr(msg)); return Qnil; @@ -280,9 +316,9 @@ static VALUE kernel_spec_rb_yield_values2(VALUE self, VALUE ary) { } static VALUE do_rec(VALUE obj, VALUE arg, int is_rec) { - if(is_rec) { + if (is_rec) { return obj; - } else if(arg == Qtrue) { + } else if (arg == Qtrue) { return rb_exec_recursive(do_rec, obj, Qnil); } else { return Qnil; @@ -302,24 +338,49 @@ static VALUE kernel_spec_rb_set_end_proc(VALUE self, VALUE io) { return Qnil; } +static void at_exit_hook(ruby_vm_t *vm) { + puts("ruby_vm_at_exit hook ran"); +} + +static VALUE kernel_spec_ruby_vm_at_exit(VALUE self) { + ruby_vm_at_exit(at_exit_hook); + return self; +} + static VALUE kernel_spec_rb_f_sprintf(VALUE self, VALUE ary) { return rb_f_sprintf((int)RARRAY_LEN(ary), RARRAY_PTR(ary)); } +static VALUE kernel_spec_rb_str_format(VALUE self, VALUE count, VALUE ary, VALUE format) { + return rb_str_format(FIX2INT(count), RARRAY_PTR(ary), format); +} + static VALUE kernel_spec_rb_make_backtrace(VALUE self) { return rb_make_backtrace(); } -static VALUE kernel_spec_rb_obj_method(VALUE self, VALUE obj, VALUE method) { - return rb_obj_method(obj, method); +static VALUE kernel_spec_rb_funcallv(VALUE self, VALUE obj, VALUE method, VALUE args) { + return rb_funcallv(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args)); +} + +static VALUE kernel_spec_rb_funcallv_kw(VALUE self, VALUE obj, VALUE method, VALUE args) { + return rb_funcallv_kw(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), RB_PASS_KEYWORDS); +} + +static VALUE kernel_spec_rb_keyword_given_p(int argc, VALUE *args, VALUE self) { + return rb_keyword_given_p() ? Qtrue : Qfalse; } -static VALUE kernel_spec_rb_funcall3(VALUE self, VALUE obj, VALUE method) { - return rb_funcall3(obj, SYM2ID(method), 0, NULL); +static VALUE kernel_spec_rb_funcallv_public(VALUE self, VALUE obj, VALUE method) { + return rb_funcallv_public(obj, SYM2ID(method), 0, NULL); } -static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE block) { - return rb_funcall_with_block(obj, SYM2ID(method), 0, NULL, block); +static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE args, VALUE block) { + return rb_funcall_with_block(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), block); +} + +static VALUE kernel_spec_rb_funcall_with_block_kw(VALUE self, VALUE obj, VALUE method, VALUE args, VALUE block) { + return rb_funcall_with_block_kw(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), block, RB_PASS_KEYWORDS); } static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE method) { @@ -329,6 +390,15 @@ static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE metho INT2FIX(5), INT2FIX(4), INT2FIX(3), INT2FIX(2), INT2FIX(1)); } +static VALUE kernel_spec_rb_check_funcall(VALUE self, VALUE receiver, VALUE method, VALUE args) { + VALUE ret = rb_check_funcall(receiver, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args)); + if (ret == Qundef) { + return ID2SYM(rb_intern("Qundef")); + } else { + return ret; + } +} + void Init_kernel_spec(void) { VALUE cls = rb_define_class("CApiKernelSpecs", rb_cObject); rb_define_method(cls, "rb_block_given_p", kernel_spec_rb_block_given_p, 0); @@ -341,20 +411,30 @@ void Init_kernel_spec(void) { rb_define_method(cls, "rb_block_lambda", kernel_spec_rb_block_lambda, 0); rb_define_method(cls, "rb_frame_this_func_test", kernel_spec_rb_frame_this_func, 0); rb_define_method(cls, "rb_frame_this_func_test_again", kernel_spec_rb_frame_this_func, 0); + rb_define_method(cls, "rb_category_warn_deprecated", kernel_spec_rb_category_warn_deprecated, 0); + rb_define_method(cls, "rb_category_warn_deprecated_with_integer_extra_value", kernel_spec_rb_category_warn_deprecated_with_integer_extra_value, 1); rb_define_method(cls, "rb_ensure", kernel_spec_rb_ensure, 4); rb_define_method(cls, "rb_eval_string", kernel_spec_rb_eval_string, 1); +#ifndef RUBY_VERSION_IS_4_0 + rb_define_method(cls, "rb_eval_cmd_kw", kernel_spec_rb_eval_cmd_kw, 3); +#endif rb_define_method(cls, "rb_raise", kernel_spec_rb_raise, 1); rb_define_method(cls, "rb_throw", kernel_spec_rb_throw, 1); rb_define_method(cls, "rb_throw_obj", kernel_spec_rb_throw_obj, 2); + rb_define_method(cls, "rb_errinfo", kernel_spec_rb_errinfo, 0); + rb_define_method(cls, "rb_set_errinfo", kernel_spec_rb_set_errinfo, 1); + 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_protect_yield", kernel_spec_rb_protect_yield, 2); + rb_define_method(cls, "rb_protect_ignore_status", kernel_spec_rb_protect_ignore_status, 2); 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_catch", kernel_spec_rb_catch, 2); rb_define_method(cls, "rb_catch_obj", kernel_spec_rb_catch_obj, 2); rb_define_method(cls, "rb_sys_fail", kernel_spec_rb_sys_fail, 1); rb_define_method(cls, "rb_syserr_fail", kernel_spec_rb_syserr_fail, 2); + rb_define_method(cls, "rb_syserr_fail_str", kernel_spec_rb_syserr_fail_str, 2); rb_define_method(cls, "rb_warn", kernel_spec_rb_warn, 1); rb_define_method(cls, "rb_yield", kernel_spec_rb_yield, 1); rb_define_method(cls, "rb_yield_indirected", kernel_spec_rb_yield_indirected, 1); @@ -364,12 +444,18 @@ void Init_kernel_spec(void) { rb_define_method(cls, "rb_yield_splat", kernel_spec_rb_yield_splat, 1); rb_define_method(cls, "rb_exec_recursive", kernel_spec_rb_exec_recursive, 1); rb_define_method(cls, "rb_set_end_proc", kernel_spec_rb_set_end_proc, 1); + rb_define_method(cls, "ruby_vm_at_exit", kernel_spec_ruby_vm_at_exit, 0); rb_define_method(cls, "rb_f_sprintf", kernel_spec_rb_f_sprintf, 1); + rb_define_method(cls, "rb_str_format", kernel_spec_rb_str_format, 3); rb_define_method(cls, "rb_make_backtrace", kernel_spec_rb_make_backtrace, 0); - rb_define_method(cls, "rb_obj_method", kernel_spec_rb_obj_method, 2); - rb_define_method(cls, "rb_funcall3", kernel_spec_rb_funcall3, 2); + rb_define_method(cls, "rb_funcallv", kernel_spec_rb_funcallv, 3); + rb_define_method(cls, "rb_funcallv_kw", kernel_spec_rb_funcallv_kw, 3); + rb_define_method(cls, "rb_keyword_given_p", kernel_spec_rb_keyword_given_p, -1); + rb_define_method(cls, "rb_funcallv_public", kernel_spec_rb_funcallv_public, 2); rb_define_method(cls, "rb_funcall_many_args", kernel_spec_rb_funcall_many_args, 2); - rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3); + rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 4); + rb_define_method(cls, "rb_funcall_with_block_kw", kernel_spec_rb_funcall_with_block_kw, 4); + rb_define_method(cls, "rb_check_funcall", kernel_spec_rb_check_funcall, 3); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/module_spec.c b/spec/ruby/optional/capi/ext/module_spec.c index 3c8455a942..12bcf99983 100644 --- a/spec/ruby/optional/capi/ext/module_spec.c +++ b/spec/ruby/optional/capi/ext/module_spec.c @@ -76,6 +76,27 @@ static VALUE module_specs_rb_define_method(VALUE self, VALUE cls, VALUE str_name return Qnil; } +static VALUE module_specs_method_var_args_1(int argc, VALUE *argv, VALUE self) { + VALUE ary = rb_ary_new(); + int i; + for (i = 0; i < argc; i++) { + rb_ary_push(ary, argv[i]); + } + return ary; +} + +static VALUE module_specs_method_var_args_2(VALUE self, VALUE argv) { + return argv; +} + +static VALUE module_specs_rb_define_method_1required(VALUE self, VALUE arg1) { + return arg1; +} + +static VALUE module_specs_rb_define_method_2required(VALUE self, VALUE arg1, VALUE arg2) { + return arg2; +} + 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); return Qnil; @@ -128,21 +149,21 @@ void Init_module_spec(void) { rb_define_method(cls, "rb_define_module_under", module_specs_rb_define_module_under, 2); rb_define_method(cls, "rb_define_const", module_specs_define_const, 3); rb_define_method(cls, "rb_define_global_const", module_specs_define_global_const, 2); - rb_define_method(cls, "rb_define_global_function", - module_specs_rb_define_global_function, 1); + rb_define_method(cls, "rb_define_global_function", 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_module_function", - module_specs_rb_define_module_function, 2); + rb_define_method(cls, "rb_define_method_varargs_1", module_specs_method_var_args_1, -1); + rb_define_method(cls, "rb_define_method_varargs_2", module_specs_method_var_args_2, -2); + rb_define_method(cls, "rb_define_method_1required", module_specs_rb_define_method_1required, 1); + rb_define_method(cls, "rb_define_method_2required", module_specs_rb_define_method_2required, 2); + + rb_define_method(cls, "rb_define_module_function", module_specs_rb_define_module_function, 2); - rb_define_method(cls, "rb_define_private_method", - module_specs_rb_define_private_method, 2); + rb_define_method(cls, "rb_define_private_method", module_specs_rb_define_private_method, 2); - rb_define_method(cls, "rb_define_protected_method", - module_specs_rb_define_protected_method, 2); + rb_define_method(cls, "rb_define_protected_method", module_specs_rb_define_protected_method, 2); - rb_define_method(cls, "rb_define_singleton_method", - module_specs_rb_define_singleton_method, 2); + rb_define_method(cls, "rb_define_singleton_method", module_specs_rb_define_singleton_method, 2); rb_define_method(cls, "rb_undef_method", module_specs_rb_undef_method, 2); rb_define_method(cls, "rb_undef", module_specs_rb_undef, 2); diff --git a/spec/ruby/optional/capi/ext/mutex_spec.c b/spec/ruby/optional/capi/ext/mutex_spec.c index c2fdf917ac..d2c8f98e89 100644 --- a/spec/ruby/optional/capi/ext/mutex_spec.c +++ b/spec/ruby/optional/capi/ext/mutex_spec.c @@ -29,15 +29,34 @@ VALUE mutex_spec_rb_mutex_sleep(VALUE self, VALUE mutex, VALUE timeout) { return rb_mutex_sleep(mutex, timeout); } - VALUE mutex_spec_rb_mutex_callback(VALUE arg) { return rb_funcall(arg, rb_intern("call"), 0); } +VALUE mutex_spec_rb_mutex_naughty_callback(VALUE arg) { + int *result = (int *) arg; + return (VALUE) result; +} + +VALUE mutex_spec_rb_mutex_callback_basic(VALUE arg) { + return arg; +} + VALUE mutex_spec_rb_mutex_synchronize(VALUE self, VALUE mutex, VALUE value) { return rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_callback, value); } +VALUE mutex_spec_rb_mutex_synchronize_with_naughty_callback(VALUE self, VALUE mutex) { + // a naughty callback accepts or returns not a Ruby object but arbitrary value + int arg = 42; + VALUE result = rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_naughty_callback, (VALUE) &arg); + return INT2NUM(*((int *) result)); +} + +VALUE mutex_spec_rb_mutex_synchronize_with_native_callback(VALUE self, VALUE mutex, VALUE value) { + return rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_callback_basic, value); +} + void Init_mutex_spec(void) { VALUE cls = rb_define_class("CApiMutexSpecs", rb_cObject); rb_define_method(cls, "rb_mutex_new", mutex_spec_rb_mutex_new, 0); @@ -47,6 +66,8 @@ void Init_mutex_spec(void) { rb_define_method(cls, "rb_mutex_unlock", mutex_spec_rb_mutex_unlock, 1); rb_define_method(cls, "rb_mutex_sleep", mutex_spec_rb_mutex_sleep, 2); rb_define_method(cls, "rb_mutex_synchronize", mutex_spec_rb_mutex_synchronize, 2); + rb_define_method(cls, "rb_mutex_synchronize_with_naughty_callback", mutex_spec_rb_mutex_synchronize_with_naughty_callback, 1); + rb_define_method(cls, "rb_mutex_synchronize_with_native_callback", mutex_spec_rb_mutex_synchronize_with_native_callback, 2); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/object_spec.c b/spec/ruby/optional/capi/ext/object_spec.c index fbdc19954f..995bc38fcf 100644 --- a/spec/ruby/optional/capi/ext/object_spec.c +++ b/spec/ruby/optional/capi/ext/object_spec.c @@ -15,9 +15,7 @@ static VALUE object_spec_FL_ABLE(VALUE self, VALUE obj) { static int object_spec_FL_TEST_flag(VALUE flag_string) { char *flag_cstr = StringValueCStr(flag_string); - if (strcmp(flag_cstr, "FL_TAINT") == 0) { - return FL_TAINT; - } else if (strcmp(flag_cstr, "FL_FREEZE") == 0) { + if (strcmp(flag_cstr, "FL_FREEZE") == 0) { return FL_FREEZE; } return 0; @@ -27,20 +25,6 @@ static VALUE object_spec_FL_TEST(VALUE self, VALUE obj, VALUE flag) { return INT2FIX(FL_TEST(obj, object_spec_FL_TEST_flag(flag))); } -static VALUE object_spec_OBJ_TAINT(VALUE self, VALUE obj) { - OBJ_TAINT(obj); - return Qnil; -} - -static VALUE object_spec_OBJ_TAINTED(VALUE self, VALUE obj) { - return OBJ_TAINTED(obj) ? Qtrue : Qfalse; -} - -static VALUE object_spec_OBJ_INFECT(VALUE self, VALUE host, VALUE source) { - OBJ_INFECT(host, source); - return Qnil; -} - static VALUE object_spec_rb_any_to_s(VALUE self, VALUE obj) { return rb_any_to_s(obj); } @@ -113,11 +97,14 @@ static VALUE so_rb_obj_call_init(VALUE self, VALUE object, return Qnil; } +static VALUE so_rb_obj_class(VALUE self, VALUE obj) { + return rb_obj_class(obj); +} + static VALUE so_rbobjclassname(VALUE self, VALUE obj) { return rb_str_new2(rb_obj_classname(obj)); } - static VALUE object_spec_rb_obj_freeze(VALUE self, VALUE obj) { return rb_obj_freeze(obj); } @@ -142,8 +129,8 @@ static VALUE object_specs_rb_obj_method_arity(VALUE self, VALUE obj, VALUE mid) return INT2FIX(rb_obj_method_arity(obj, SYM2ID(mid))); } -static VALUE object_spec_rb_obj_taint(VALUE self, VALUE obj) { - return rb_obj_taint(obj); +static VALUE object_specs_rb_obj_method(VALUE self, VALUE obj, VALUE method) { + return rb_obj_method(obj, method); } static VALUE so_require(VALUE self) { @@ -165,11 +152,7 @@ static VALUE object_spec_rb_method_boundp(VALUE self, VALUE obj, VALUE method, V } static VALUE object_spec_rb_special_const_p(VALUE self, VALUE value) { - if (rb_special_const_p(value)) { - return Qtrue; - } else { - return Qfalse; - } + return rb_special_const_p(value); } static VALUE so_to_id(VALUE self, VALUE obj) { @@ -180,120 +163,132 @@ static VALUE object_spec_RTEST(VALUE self, VALUE value) { return RTEST(value) ? Qtrue : Qfalse; } +static VALUE so_check_type(VALUE self, VALUE obj, VALUE other) { + rb_check_type(obj, TYPE(other)); + return Qtrue; +} + static VALUE so_is_type_nil(VALUE self, VALUE obj) { - if(TYPE(obj) == T_NIL) { + if (TYPE(obj) == T_NIL) { return Qtrue; } return Qfalse; } static VALUE so_is_type_object(VALUE self, VALUE obj) { - if(TYPE(obj) == T_OBJECT) { + if (TYPE(obj) == T_OBJECT) { return Qtrue; } return Qfalse; } static VALUE so_is_type_array(VALUE self, VALUE obj) { - if(TYPE(obj) == T_ARRAY) { + if (TYPE(obj) == T_ARRAY) { return Qtrue; } return Qfalse; } static VALUE so_is_type_module(VALUE self, VALUE obj) { - if(TYPE(obj) == T_MODULE) { + if (TYPE(obj) == T_MODULE) { return Qtrue; } return Qfalse; } static VALUE so_is_type_class(VALUE self, VALUE obj) { - if(TYPE(obj) == T_CLASS) { + if (TYPE(obj) == T_CLASS) { return Qtrue; } return Qfalse; } static VALUE so_is_type_data(VALUE self, VALUE obj) { - if(TYPE(obj) == T_DATA) { + if (TYPE(obj) == T_DATA) { return Qtrue; } return Qfalse; } static VALUE so_is_rb_type_p_nil(VALUE self, VALUE obj) { - if(rb_type_p(obj, T_NIL)) { + if (rb_type_p(obj, T_NIL)) { return Qtrue; } return Qfalse; } static VALUE so_is_rb_type_p_object(VALUE self, VALUE obj) { - if(rb_type_p(obj, T_OBJECT)) { + if (rb_type_p(obj, T_OBJECT)) { return Qtrue; } return Qfalse; } static VALUE so_is_rb_type_p_array(VALUE self, VALUE obj) { - if(rb_type_p(obj, T_ARRAY)) { + if (rb_type_p(obj, T_ARRAY)) { return Qtrue; } return Qfalse; } static VALUE so_is_rb_type_p_module(VALUE self, VALUE obj) { - if(rb_type_p(obj, T_MODULE)) { + if (rb_type_p(obj, T_MODULE)) { return Qtrue; } return Qfalse; } static VALUE so_is_rb_type_p_class(VALUE self, VALUE obj) { - if(rb_type_p(obj, T_CLASS)) { + if (rb_type_p(obj, T_CLASS)) { return Qtrue; } return Qfalse; } static VALUE so_is_rb_type_p_data(VALUE self, VALUE obj) { - if(rb_type_p(obj, T_DATA)) { + if (rb_type_p(obj, T_DATA)) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_rb_type_p_file(VALUE self, VALUE obj) { + if (rb_type_p(obj, T_FILE)) { return Qtrue; } return Qfalse; } static VALUE so_is_builtin_type_object(VALUE self, VALUE obj) { - if(BUILTIN_TYPE(obj) == T_OBJECT) { + if (BUILTIN_TYPE(obj) == T_OBJECT) { return Qtrue; } return Qfalse; } static VALUE so_is_builtin_type_array(VALUE self, VALUE obj) { - if(BUILTIN_TYPE(obj) == T_ARRAY) { + if (BUILTIN_TYPE(obj) == T_ARRAY) { return Qtrue; } return Qfalse; } static VALUE so_is_builtin_type_module(VALUE self, VALUE obj) { - if(BUILTIN_TYPE(obj) == T_MODULE) { + if (BUILTIN_TYPE(obj) == T_MODULE) { return Qtrue; } return Qfalse; } static VALUE so_is_builtin_type_class(VALUE self, VALUE obj) { - if(BUILTIN_TYPE(obj) == T_CLASS) { + if (BUILTIN_TYPE(obj) == T_CLASS) { return Qtrue; } return Qfalse; } static VALUE so_is_builtin_type_data(VALUE self, VALUE obj) { - if(BUILTIN_TYPE(obj) == T_DATA) { + if (BUILTIN_TYPE(obj) == T_DATA) { return Qtrue; } return Qfalse; @@ -349,48 +344,59 @@ static VALUE object_spec_rb_class_inherited_p(VALUE self, VALUE mod, VALUE arg) return rb_class_inherited_p(mod, arg); } +static int foreach_f(ID key, VALUE val, VALUE ary) { + rb_ary_push(ary, ID2SYM(key)); + rb_ary_push(ary, val); + return ST_CONTINUE; +} + +static VALUE object_spec_rb_ivar_foreach(VALUE self, VALUE obj) { + VALUE ary = rb_ary_new(); + rb_ivar_foreach(obj, foreach_f, ary); + return ary; +} + static VALUE speced_allocator(VALUE klass) { - VALUE flags = 0; - VALUE instance; - if (RTEST(rb_class_inherited_p(klass, rb_cString))) { - flags = T_STRING; - } else if (RTEST(rb_class_inherited_p(klass, rb_cArray))) { - flags = T_ARRAY; - } else { - flags = T_OBJECT; - } - instance = rb_newobj_of(klass, flags); + VALUE super = rb_class_get_superclass(klass); + VALUE instance = rb_get_alloc_func(super)(klass); rb_iv_set(instance, "@from_custom_allocator", Qtrue); return instance; } -static VALUE define_alloc_func(VALUE self, VALUE klass) { +static VALUE object_spec_rb_define_alloc_func(VALUE self, VALUE klass) { rb_define_alloc_func(klass, speced_allocator); return Qnil; } -static VALUE undef_alloc_func(VALUE self, VALUE klass) { +static VALUE object_spec_rb_undef_alloc_func(VALUE self, VALUE klass) { rb_undef_alloc_func(klass); return Qnil; } -static VALUE speced_allocator_p(VALUE self, VALUE klass) { +static VALUE object_spec_speced_allocator_p(VALUE self, VALUE klass) { rb_alloc_func_t allocator = rb_get_alloc_func(klass); return (allocator == speced_allocator) ? Qtrue : Qfalse; } -static VALUE custom_alloc_func_p(VALUE self, VALUE klass) { +static VALUE object_spec_custom_alloc_func_p(VALUE self, VALUE klass) { rb_alloc_func_t allocator = rb_get_alloc_func(klass); return allocator ? Qtrue : Qfalse; } +static VALUE object_spec_redefine_frozen(VALUE self) { + // The purpose of this spec is to verify that `frozen?` + // and `RB_OBJ_FROZEN` do not mutually recurse infinitely. + if (RB_OBJ_FROZEN(self)) { + return Qtrue; + } + + return Qfalse; +} + void Init_object_spec(void) { VALUE cls = rb_define_class("CApiObjectSpecs", rb_cObject); rb_define_method(cls, "FL_ABLE", object_spec_FL_ABLE, 1); rb_define_method(cls, "FL_TEST", object_spec_FL_TEST, 2); - rb_define_method(cls, "OBJ_TAINT", object_spec_OBJ_TAINT, 1); - rb_define_method(cls, "OBJ_TAINTED", object_spec_OBJ_TAINTED, 1); - rb_define_method(cls, "OBJ_INFECT", object_spec_OBJ_INFECT, 2); rb_define_method(cls, "rb_any_to_s", object_spec_rb_any_to_s, 1); rb_define_method(cls, "rb_attr_get", so_attr_get, 2); rb_define_method(cls, "rb_obj_instance_variables", object_spec_rb_obj_instance_variables, 1); @@ -406,6 +412,7 @@ void Init_object_spec(void) { rb_define_method(cls, "rb_obj_alloc", so_rb_obj_alloc, 1); rb_define_method(cls, "rb_obj_dup", so_rb_obj_dup, 1); rb_define_method(cls, "rb_obj_call_init", so_rb_obj_call_init, 3); + rb_define_method(cls, "rb_obj_class", so_rb_obj_class, 1); rb_define_method(cls, "rb_obj_classname", so_rbobjclassname, 1); rb_define_method(cls, "rb_obj_freeze", object_spec_rb_obj_freeze, 1); rb_define_method(cls, "rb_obj_frozen_p", object_spec_rb_obj_frozen_p, 1); @@ -413,15 +420,15 @@ void Init_object_spec(void) { rb_define_method(cls, "rb_obj_is_instance_of", so_instance_of, 2); rb_define_method(cls, "rb_obj_is_kind_of", so_kind_of, 2); rb_define_method(cls, "rb_obj_method_arity", object_specs_rb_obj_method_arity, 2); - rb_define_method(cls, "rb_obj_taint", object_spec_rb_obj_taint, 1); + rb_define_method(cls, "rb_obj_method", object_specs_rb_obj_method, 2); rb_define_method(cls, "rb_require", so_require, 0); rb_define_method(cls, "rb_respond_to", so_respond_to, 2); rb_define_method(cls, "rb_method_boundp", object_spec_rb_method_boundp, 3); rb_define_method(cls, "rb_obj_respond_to", so_obj_respond_to, 3); rb_define_method(cls, "rb_special_const_p", object_spec_rb_special_const_p, 1); - rb_define_method(cls, "rb_to_id", so_to_id, 1); rb_define_method(cls, "RTEST", object_spec_RTEST, 1); + rb_define_method(cls, "rb_check_type", so_check_type, 2); rb_define_method(cls, "rb_is_type_nil", so_is_type_nil, 1); rb_define_method(cls, "rb_is_type_object", so_is_type_object, 1); rb_define_method(cls, "rb_is_type_array", so_is_type_array, 1); @@ -434,6 +441,7 @@ void Init_object_spec(void) { rb_define_method(cls, "rb_is_rb_type_p_module", so_is_rb_type_p_module, 1); rb_define_method(cls, "rb_is_rb_type_p_class", so_is_rb_type_p_class, 1); rb_define_method(cls, "rb_is_rb_type_p_data", so_is_rb_type_p_data, 1); + rb_define_method(cls, "rb_is_rb_type_p_file", so_is_rb_type_p_file, 1); rb_define_method(cls, "rb_is_builtin_type_object", so_is_builtin_type_object, 1); rb_define_method(cls, "rb_is_builtin_type_array", so_is_builtin_type_array, 1); rb_define_method(cls, "rb_is_builtin_type_module", so_is_builtin_type_module, 1); @@ -451,10 +459,15 @@ void Init_object_spec(void) { rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2); rb_define_method(cls, "rb_copy_generic_ivar", object_spec_rb_copy_generic_ivar, 2); rb_define_method(cls, "rb_free_generic_ivar", object_spec_rb_free_generic_ivar, 1); - rb_define_method(cls, "rb_define_alloc_func", define_alloc_func, 1); - rb_define_method(cls, "rb_undef_alloc_func", undef_alloc_func, 1); - rb_define_method(cls, "speced_allocator?", speced_allocator_p, 1); - rb_define_method(cls, "custom_alloc_func?", custom_alloc_func_p, 1); + rb_define_method(cls, "rb_define_alloc_func", object_spec_rb_define_alloc_func, 1); + rb_define_method(cls, "rb_undef_alloc_func", object_spec_rb_undef_alloc_func, 1); + rb_define_method(cls, "speced_allocator?", object_spec_speced_allocator_p, 1); + rb_define_method(cls, "custom_alloc_func?", object_spec_custom_alloc_func_p, 1); + rb_define_method(cls, "not_implemented_method", rb_f_notimplement, -1); + rb_define_method(cls, "rb_ivar_foreach", object_spec_rb_ivar_foreach, 1); + + cls = rb_define_class("CApiObjectRedefinitionSpecs", rb_cObject); + rb_define_method(cls, "frozen?", object_spec_redefine_frozen, 0); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/proc_spec.c b/spec/ruby/optional/capi/ext/proc_spec.c index e0bd8b1bbc..b7cd5d6262 100644 --- a/spec/ruby/optional/capi/ext/proc_spec.c +++ b/spec/ruby/optional/capi/ext/proc_spec.c @@ -11,10 +11,63 @@ VALUE proc_spec_rb_proc_new_function(RB_BLOCK_CALL_FUNC_ARGLIST(args, dummy)) { return rb_funcall(args, rb_intern("inspect"), 0); } +VALUE proc_spec_rb_proc_new_function_arg(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) { + return arg; +} + +VALUE proc_spec_rb_proc_new_function_argc(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) { + return INT2FIX(argc); +} + +VALUE proc_spec_rb_proc_new_function_argv_n(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) { + int n = FIX2INT(arg); + if (n < argc) { + return argv[n]; + } else { + rb_exc_raise(rb_exc_new2(rb_eArgError, "Arg index out of bounds.")); + } +} + +VALUE proc_spec_rb_proc_new_function_callback_arg(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) { + return callback_arg; +} + +VALUE proc_spec_rb_proc_new_function_blockarg(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) { + return blockarg; +} + +VALUE proc_spec_rb_proc_new_function_block_given_p(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) { + return rb_block_given_p() ? Qtrue : Qfalse; +} + VALUE proc_spec_rb_proc_new(VALUE self) { return rb_proc_new(proc_spec_rb_proc_new_function, Qnil); } +VALUE proc_spec_rb_proc_new_arg(VALUE self) { + return rb_proc_new(proc_spec_rb_proc_new_function_arg, Qnil); +} + +VALUE proc_spec_rb_proc_new_argc(VALUE self) { + return rb_proc_new(proc_spec_rb_proc_new_function_argc, Qnil); +} + +VALUE proc_spec_rb_proc_new_argv_n(VALUE self) { + return rb_proc_new(proc_spec_rb_proc_new_function_argv_n, Qnil); +} + +VALUE proc_spec_rb_proc_new_callback_arg(VALUE self, VALUE arg) { + return rb_proc_new(proc_spec_rb_proc_new_function_callback_arg, arg); +} + +VALUE proc_spec_rb_proc_new_blockarg(VALUE self) { + return rb_proc_new(proc_spec_rb_proc_new_function_blockarg, Qnil); +} + +VALUE proc_spec_rb_proc_new_block_given_p(VALUE self) { + return rb_proc_new(proc_spec_rb_proc_new_function_block_given_p, Qnil); +} + VALUE proc_spec_rb_proc_arity(VALUE self, VALUE prc) { return INT2FIX(rb_proc_arity(prc)); } @@ -23,6 +76,18 @@ VALUE proc_spec_rb_proc_call(VALUE self, VALUE prc, VALUE args) { return rb_proc_call(prc, args); } +VALUE proc_spec_rb_proc_call_kw(VALUE self, VALUE prc, VALUE args) { + return rb_proc_call_kw(prc, args, RB_PASS_KEYWORDS); +} + +VALUE proc_spec_rb_proc_call_with_block(VALUE self, VALUE prc, VALUE args, VALUE block) { + return rb_proc_call_with_block(prc, RARRAY_LENINT(args), RARRAY_PTR(args), block); +} + +static VALUE proc_spec_rb_proc_call_with_block_kw(VALUE self, VALUE prc, VALUE args, VALUE block) { + return rb_proc_call_with_block_kw(prc, RARRAY_LENINT(args), RARRAY_PTR(args), block, RB_PASS_KEYWORDS); +} + VALUE proc_spec_rb_obj_is_proc(VALUE self, VALUE prc) { return rb_obj_is_proc(prc); } @@ -62,8 +127,17 @@ VALUE proc_spec_rb_Proc_new(VALUE self, VALUE scenario) { void Init_proc_spec(void) { VALUE cls = rb_define_class("CApiProcSpecs", rb_cObject); rb_define_method(cls, "rb_proc_new", proc_spec_rb_proc_new, 0); + rb_define_method(cls, "rb_proc_new_arg", proc_spec_rb_proc_new_arg, 0); + rb_define_method(cls, "rb_proc_new_argc", proc_spec_rb_proc_new_argc, 0); + rb_define_method(cls, "rb_proc_new_argv_n", proc_spec_rb_proc_new_argv_n, 0); + rb_define_method(cls, "rb_proc_new_callback_arg", proc_spec_rb_proc_new_callback_arg, 1); + rb_define_method(cls, "rb_proc_new_blockarg", proc_spec_rb_proc_new_blockarg, 0); + rb_define_method(cls, "rb_proc_new_block_given_p", proc_spec_rb_proc_new_block_given_p, 0); rb_define_method(cls, "rb_proc_arity", proc_spec_rb_proc_arity, 1); rb_define_method(cls, "rb_proc_call", proc_spec_rb_proc_call, 2); + rb_define_method(cls, "rb_proc_call_kw", proc_spec_rb_proc_call_kw, 2); + rb_define_method(cls, "rb_proc_call_with_block", proc_spec_rb_proc_call_with_block, 3); + rb_define_method(cls, "rb_proc_call_with_block_kw", proc_spec_rb_proc_call_with_block_kw, 3); rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1); rb_define_method(cls, "rb_obj_is_proc", proc_spec_rb_obj_is_proc, 1); } diff --git a/spec/ruby/optional/capi/ext/range_spec.c b/spec/ruby/optional/capi/ext/range_spec.c index 7a475ec695..9faed3e5ee 100644 --- a/spec/ruby/optional/capi/ext/range_spec.c +++ b/spec/ruby/optional/capi/ext/range_spec.c @@ -7,7 +7,7 @@ extern "C" { VALUE range_spec_rb_range_new(int argc, VALUE* argv, VALUE self) { int exclude_end = 0; - if(argc == 3) { + if (argc == 3) { exclude_end = RTEST(argv[2]); } return rb_range_new(argv[0], argv[1], exclude_end); @@ -25,9 +25,9 @@ VALUE range_spec_rb_range_values(VALUE self, VALUE range) { return ary; } -VALUE range_spec_rb_range_beg_len(VALUE self, VALUE range, VALUE begpv, VALUE lenpv, VALUE lenv, VALUE errv) { - long begp = FIX2LONG(begpv); - long lenp = FIX2LONG(lenpv); +VALUE range_spec_rb_range_beg_len(VALUE self, VALUE range, VALUE lenv, VALUE errv) { + long begp = 0; + long lenp = 0; long len = FIX2LONG(lenv); int err = FIX2INT(errv); VALUE ary = rb_ary_new(); @@ -38,11 +38,51 @@ VALUE range_spec_rb_range_beg_len(VALUE self, VALUE range, VALUE begpv, VALUE le return ary; } +VALUE range_spec_rb_arithmetic_sequence_extract(VALUE self, VALUE object) { + VALUE ary = rb_ary_new(); + rb_arithmetic_sequence_components_t components; + + int status = rb_arithmetic_sequence_extract(object, &components); + + if (!status) { + rb_ary_store(ary, 0, LONG2FIX(status)); + return ary; + } + + rb_ary_store(ary, 0, LONG2FIX(status)); + rb_ary_store(ary, 1, components.begin); + rb_ary_store(ary, 2, components.end); + rb_ary_store(ary, 3, components.step); + rb_ary_store(ary, 4, components.exclude_end ? Qtrue : Qfalse); + return ary; +} + +VALUE range_spec_rb_arithmetic_sequence_beg_len_step(VALUE self, VALUE aseq, VALUE lenv, VALUE errv) { + long begp = 0; + long lenp = 0; + long stepp = 0; + + long len = FIX2LONG(lenv); + int err = FIX2INT(errv); + + VALUE success = rb_arithmetic_sequence_beg_len_step(aseq, &begp, &lenp, &stepp, len, err); + + VALUE ary = rb_ary_new(); + rb_ary_store(ary, 0, success); + rb_ary_store(ary, 1, LONG2FIX(begp)); + rb_ary_store(ary, 2, LONG2FIX(lenp)); + rb_ary_store(ary, 3, LONG2FIX(stepp)); + + return ary; +} + void Init_range_spec(void) { VALUE cls = rb_define_class("CApiRangeSpecs", rb_cObject); rb_define_method(cls, "rb_range_new", range_spec_rb_range_new, -1); rb_define_method(cls, "rb_range_values", range_spec_rb_range_values, 1); - rb_define_method(cls, "rb_range_beg_len", range_spec_rb_range_beg_len, 5); + rb_define_method(cls, "rb_range_beg_len", range_spec_rb_range_beg_len, 3); + rb_define_method(cls, "rb_arithmetic_sequence_extract", range_spec_rb_arithmetic_sequence_extract, 1); + rb_define_method(cls, "rb_arithmetic_sequence_beg_len_step", range_spec_rb_arithmetic_sequence_beg_len_step, 3); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/rbasic_spec.c b/spec/ruby/optional/capi/ext/rbasic_spec.c index cf10904294..5a95b92804 100644 --- a/spec/ruby/optional/capi/ext/rbasic_spec.c +++ b/spec/ruby/optional/capi/ext/rbasic_spec.c @@ -5,10 +5,20 @@ extern "C" { #endif +#ifndef RBASIC_FLAGS +#define RBASIC_FLAGS(obj) (RBASIC(obj)->flags) +#endif + +#ifndef RBASIC_SET_FLAGS +#define RBASIC_SET_FLAGS(obj, flags_to_set) (RBASIC(obj)->flags = flags_to_set) +#endif + #ifndef FL_SHAREABLE -static const VALUE VISIBLE_BITS = FL_TAINT | FL_FREEZE | ~(FL_USER0 - 1); +static const VALUE VISIBLE_BITS = FL_TAINT | FL_FREEZE; +static const VALUE DATA_VISIBLE_BITS = FL_TAINT | FL_FREEZE | ~(FL_USER0 - 1); #else -static const VALUE VISIBLE_BITS = FL_FREEZE | ~(FL_USER0 - 1); +static const VALUE VISIBLE_BITS = FL_FREEZE; +static const VALUE DATA_VISIBLE_BITS = FL_FREEZE | ~(FL_USER0 - 1); #endif #if SIZEOF_VALUE == SIZEOF_LONG @@ -21,61 +31,61 @@ static const VALUE VISIBLE_BITS = FL_FREEZE | ~(FL_USER0 - 1); #error "unsupported" #endif - -VALUE rbasic_spec_taint_flag(VALUE self) { - return VALUE2NUM(RUBY_FL_TAINT); -} - VALUE rbasic_spec_freeze_flag(VALUE self) { return VALUE2NUM(RUBY_FL_FREEZE); } -static VALUE spec_get_flags(const struct RBasic *b) { - VALUE flags = b->flags & VISIBLE_BITS; +static VALUE spec_get_flags(VALUE obj, VALUE visible_bits) { + VALUE flags = RB_FL_TEST(obj, visible_bits); return VALUE2NUM(flags); } -static VALUE spec_set_flags(struct RBasic *b, VALUE flags) { - flags &= VISIBLE_BITS; - b->flags = (b->flags & ~VISIBLE_BITS) | flags; +static VALUE spec_set_flags(VALUE obj, VALUE flags, VALUE visible_bits) { + flags &= visible_bits; + + // Could also be done like: + // RB_FL_UNSET(obj, visible_bits); + // RB_FL_SET(obj, flags); + // But that seems rather indirect + RBASIC_SET_FLAGS(obj, (RBASIC_FLAGS(obj) & ~visible_bits) | flags); + return VALUE2NUM(flags); } -VALUE rbasic_spec_get_flags(VALUE self, VALUE val) { - return spec_get_flags(RBASIC(val)); +static VALUE rbasic_spec_get_flags(VALUE self, VALUE obj) { + return spec_get_flags(obj, VISIBLE_BITS); } -VALUE rbasic_spec_set_flags(VALUE self, VALUE val, VALUE flags) { - return spec_set_flags(RBASIC(val), NUM2VALUE(flags)); +static VALUE rbasic_spec_set_flags(VALUE self, VALUE obj, VALUE flags) { + return spec_set_flags(obj, NUM2VALUE(flags), VISIBLE_BITS); } -VALUE rbasic_spec_copy_flags(VALUE self, VALUE to, VALUE from) { - return spec_set_flags(RBASIC(to), RBASIC(from)->flags); +static VALUE rbasic_spec_copy_flags(VALUE self, VALUE to, VALUE from) { + return spec_set_flags(to, RBASIC_FLAGS(from), VISIBLE_BITS); } -VALUE rbasic_spec_get_klass(VALUE self, VALUE val) { - return RBASIC(val)->klass; +static VALUE rbasic_spec_get_klass(VALUE self, VALUE obj) { + return RBASIC_CLASS(obj); } -VALUE rbasic_rdata_spec_get_flags(VALUE self, VALUE structure) { - return spec_get_flags(&RDATA(structure)->basic); +static VALUE rbasic_rdata_spec_get_flags(VALUE self, VALUE structure) { + return spec_get_flags(structure, DATA_VISIBLE_BITS); } -VALUE rbasic_rdata_spec_set_flags(VALUE self, VALUE structure, VALUE flags) { - return spec_set_flags(&RDATA(structure)->basic, NUM2VALUE(flags)); +static VALUE rbasic_rdata_spec_set_flags(VALUE self, VALUE structure, VALUE flags) { + return spec_set_flags(structure, NUM2VALUE(flags), DATA_VISIBLE_BITS); } -VALUE rbasic_rdata_spec_copy_flags(VALUE self, VALUE to, VALUE from) { - return spec_set_flags(&RDATA(to)->basic, RDATA(from)->basic.flags); +static VALUE rbasic_rdata_spec_copy_flags(VALUE self, VALUE to, VALUE from) { + return spec_set_flags(to, RBASIC_FLAGS(from), DATA_VISIBLE_BITS); } -VALUE rbasic_rdata_spec_get_klass(VALUE self, VALUE structure) { - return RDATA(structure)->basic.klass; +static VALUE rbasic_rdata_spec_get_klass(VALUE self, VALUE structure) { + return RBASIC_CLASS(structure); } void Init_rbasic_spec(void) { VALUE cls = rb_define_class("CApiRBasicSpecs", rb_cObject); - rb_define_method(cls, "taint_flag", rbasic_spec_taint_flag, 0); rb_define_method(cls, "freeze_flag", rbasic_spec_freeze_flag, 0); rb_define_method(cls, "get_flags", rbasic_spec_get_flags, 1); rb_define_method(cls, "set_flags", rbasic_spec_set_flags, 2); diff --git a/spec/ruby/optional/capi/ext/regexp_spec.c b/spec/ruby/optional/capi/ext/regexp_spec.c index 70780bae2a..9de7982b50 100644 --- a/spec/ruby/optional/capi/ext/regexp_spec.c +++ b/spec/ruby/optional/capi/ext/regexp_spec.c @@ -35,6 +35,11 @@ VALUE regexp_spec_backref_get(VALUE self) { return rb_backref_get(); } +static VALUE regexp_spec_backref_set(VALUE self, VALUE backref) { + rb_backref_set(backref); + return Qnil; +} + VALUE regexp_spec_reg_match_backref_get(VALUE self, VALUE re, VALUE str) { rb_reg_match(re, str); return rb_backref_get(); @@ -44,6 +49,12 @@ VALUE regexp_spec_match(VALUE self, VALUE regexp, VALUE str) { return rb_funcall(regexp, rb_intern("match"), 1, str); } +VALUE regexp_spec_memcicmp(VALUE self, VALUE str1, VALUE str2) { + long l1 = RSTRING_LEN(str1); + long l2 = RSTRING_LEN(str2); + return INT2FIX(rb_memcicmp(RSTRING_PTR(str1), RSTRING_PTR(str2), l1 < l2 ? l1 : l2)); +} + void Init_regexp_spec(void) { VALUE cls = rb_define_class("CApiRegexpSpecs", rb_cObject); rb_define_method(cls, "match", regexp_spec_match, 2); @@ -51,9 +62,11 @@ void Init_regexp_spec(void) { rb_define_method(cls, "a_re_1st_match", regexp_spec_reg_1st_match, 1); rb_define_method(cls, "rb_reg_match", regexp_spec_reg_match, 2); rb_define_method(cls, "rb_backref_get", regexp_spec_backref_get, 0); + rb_define_method(cls, "rb_backref_set", regexp_spec_backref_set, 1); rb_define_method(cls, "rb_reg_match_backref_get", regexp_spec_reg_match_backref_get, 2); rb_define_method(cls, "rb_reg_options", regexp_spec_rb_reg_options, 1); rb_define_method(cls, "rb_reg_regcomp", regexp_spec_rb_reg_regcomp, 1); + rb_define_method(cls, "rb_memcicmp", regexp_spec_memcicmp, 2); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/rubyspec.h b/spec/ruby/optional/capi/ext/rubyspec.h index b4d604e647..5a92645785 100644 --- a/spec/ruby/optional/capi/ext/rubyspec.h +++ b/spec/ruby/optional/capi/ext/rubyspec.h @@ -5,55 +5,46 @@ * guards to assist with version incompatibilities. */ #include <ruby.h> -#ifdef HAVE_RUBY_VERSION_H -# include <ruby/version.h> -#else -# include <version.h> -#endif +#include <ruby/version.h> -#ifndef RUBY_VERSION_MAJOR -#define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR -#define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR -#define RUBY_VERSION_TEENY RUBY_API_VERSION_TEENY -#endif +/* copied from ext/-test-/cxxanyargs/cxxanyargs.cpp */ +#if 0 /* Ignore deprecation warnings */ + +#elif defined(_MSC_VER) +#pragma warning(disable : 4996) + +#elif defined(__INTEL_COMPILER) +#pragma warning(disable : 1786) -#define RUBY_VERSION_BEFORE(major,minor,teeny) \ - ((RUBY_VERSION_MAJOR < (major)) || \ - (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR < (minor)) || \ - (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR == (minor) && RUBY_VERSION_TEENY < (teeny))) +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#elif defined(__SUNPRO_CC) +#pragma error_messages (off,symdeprecated) + +#else +// :FIXME: improve here for your compiler. -#if RUBY_VERSION_MAJOR > 3 || (RUBY_VERSION_MAJOR == 3 && RUBY_VERSION_MINOR >= 0) -#define RUBY_VERSION_IS_3_0 #endif -#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 7) -#define RUBY_VERSION_IS_2_7 +#define RUBY_VERSION_BEFORE(major,minor) \ + ((RUBY_API_VERSION_MAJOR < (major)) || \ + (RUBY_API_VERSION_MAJOR == (major) && RUBY_API_VERSION_MINOR < (minor))) +#define RUBY_VERSION_SINCE(major,minor) (!RUBY_VERSION_BEFORE(major, minor)) + +#if RUBY_VERSION_SINCE(4, 1) +#define RUBY_VERSION_IS_4_1 #endif -#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 6) -#define RUBY_VERSION_IS_2_6 +#if RUBY_VERSION_SINCE(4, 0) +#define RUBY_VERSION_IS_4_0 #endif -#if defined(__cplusplus) && !defined(RUBY_VERSION_IS_2_7) -/* Ruby < 2.7 needs this to let these function with callbacks and compile in C++ code */ -#define rb_define_method(mod, name, func, argc) rb_define_method(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_protected_method(mod, name, func, argc) rb_define_protected_method(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_private_method(mod, name, func, argc) rb_define_private_method(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_singleton_method(mod, name, func, argc) rb_define_singleton_method(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_module_function(mod, name, func, argc) rb_define_module_function(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_global_function(name, func, argc) rb_define_global_function(name, RUBY_METHOD_FUNC(func), argc) -#define rb_iterate(function, arg1, block, arg2) rb_iterate(function, arg1, RUBY_METHOD_FUNC(block), arg2) -#define rb_hash_foreach(hash, func, farg) rb_hash_foreach(hash, (int (*)(...))func, farg) -#define st_foreach(tab, func, arg) st_foreach(tab, (int (*)(...))func, arg) -#define rb_block_call(object, name, args_count, args, block_call_func, data) rb_block_call(object, name, args_count, args, RUBY_METHOD_FUNC(block_call_func), data) -#define rb_ensure(b_proc, data1, e_proc, data2) rb_ensure(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2) -#define rb_rescue(b_proc, data1, e_proc, data2) rb_rescue(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2) -#define rb_rescue2(b_proc, data1, e_proc, data2, ...) rb_rescue2(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2, __VA_ARGS__) -#define rb_catch(tag, func, data) rb_catch(tag, RUBY_METHOD_FUNC(func), data) -#define rb_catch_obj(tag, func, data) rb_catch_obj(tag, RUBY_METHOD_FUNC(func), data) -#define rb_proc_new(fn, arg) rb_proc_new(RUBY_METHOD_FUNC(fn), arg) -#define rb_thread_create(fn, arg) rb_thread_create(RUBY_METHOD_FUNC(fn), arg) -#define rb_define_hooked_variable(name, var, getter, setter) rb_define_hooked_variable(name, var, RUBY_METHOD_FUNC(getter), (void (*)(...))setter) +#if RUBY_VERSION_SINCE(3, 4) +#define RUBY_VERSION_IS_3_4 #endif #endif diff --git a/spec/ruby/optional/capi/ext/set_spec.c b/spec/ruby/optional/capi/ext/set_spec.c new file mode 100644 index 0000000000..11a271b361 --- /dev/null +++ b/spec/ruby/optional/capi/ext/set_spec.c @@ -0,0 +1,65 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef RUBY_VERSION_IS_4_0 +#ifdef __cplusplus +extern "C" { +#endif + +#define RBOOL(x) ((x) ? Qtrue : Qfalse) + +int yield_element_and_arg(VALUE element, VALUE arg) { + return RTEST(rb_yield_values(2, element, arg)) ? ST_CONTINUE : ST_STOP; +} + +VALUE set_spec_rb_set_foreach(VALUE self, VALUE set, VALUE arg) { + rb_set_foreach(set, yield_element_and_arg, arg); + return Qnil; +} + +VALUE set_spec_rb_set_new(VALUE self) { + return rb_set_new(); +} + +VALUE set_spec_rb_set_new_capa(VALUE self, VALUE capa) { + return rb_set_new_capa(NUM2INT(capa)); +} + +VALUE set_spec_rb_set_lookup(VALUE self, VALUE set, VALUE element) { + return RBOOL(rb_set_lookup(set, element)); +} + +VALUE set_spec_rb_set_add(VALUE self, VALUE set, VALUE element) { + return RBOOL(rb_set_add(set, element)); +} + +VALUE set_spec_rb_set_clear(VALUE self, VALUE set) { + return rb_set_clear(set); +} + +VALUE set_spec_rb_set_delete(VALUE self, VALUE set, VALUE element) { + return RBOOL(rb_set_delete(set, element)); +} + +VALUE set_spec_rb_set_size(VALUE self, VALUE set) { + return SIZET2NUM(rb_set_size(set)); +} + +void Init_set_spec(void) { + VALUE cls = rb_define_class("CApiSetSpecs", rb_cObject); + + rb_define_method(cls, "rb_set_foreach", set_spec_rb_set_foreach, 2); + rb_define_method(cls, "rb_set_new", set_spec_rb_set_new, 0); + rb_define_method(cls, "rb_set_new_capa", set_spec_rb_set_new_capa, 1); + rb_define_method(cls, "rb_set_lookup", set_spec_rb_set_lookup, 2); + rb_define_method(cls, "rb_set_add", set_spec_rb_set_add, 2); + rb_define_method(cls, "rb_set_clear", set_spec_rb_set_clear, 1); + rb_define_method(cls, "rb_set_delete", set_spec_rb_set_delete, 2); + rb_define_method(cls, "rb_set_size", set_spec_rb_set_size, 1); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/spec/ruby/optional/capi/ext/string_spec.c b/spec/ruby/optional/capi/ext/string_spec.c index dd7bc397cd..8291b73ea4 100644 --- a/spec/ruby/optional/capi/ext/string_spec.c +++ b/spec/ruby/optional/capi/ext/string_spec.c @@ -1,8 +1,10 @@ #include "ruby.h" #include "rubyspec.h" +#include <fcntl.h> #include <string.h> #include <stdarg.h> +#include <errno.h> #include "ruby/encoding.h" @@ -10,6 +12,13 @@ extern "C" { #endif +#ifdef PTR2NUM +#elif SIZEOF_VOIDP <= SIZEOF_LONG +# define PTR2NUM(x) (LONG2NUM((long)(x))) +#elif SIZEOF_VOIDP <= SIZEOF_LONG_LONG +# define PTR2NUM(x) (LL2NUM((LONG_LONG)(x))) +#endif + /* Make sure the RSTRING_PTR and the bytes are in native memory. * On TruffleRuby RSTRING_PTR and the bytes remain in managed memory * until they must be written to native memory. @@ -49,18 +58,12 @@ VALUE string_spec_rb_str_set_len_RSTRING_LEN(VALUE self, VALUE str, VALUE len) { return INT2FIX(RSTRING_LEN(str)); } -VALUE rb_fstring(VALUE str); /* internal.h, used in ripper */ - -VALUE string_spec_rb_str_fstring(VALUE self, VALUE str) { - return rb_fstring(str); -} - VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) { VALUE buf; buf = rb_str_buf_new(NUM2LONG(len)); - if(RTEST(str)) { + if (RTEST(str)) { snprintf(RSTRING_PTR(buf), NUM2LONG(len), "%s", RSTRING_PTR(str)); } @@ -85,6 +88,10 @@ VALUE string_spec_rb_str_tmp_new_klass(VALUE self, VALUE len) { return RBASIC_CLASS(rb_str_tmp_new(NUM2LONG(len))); } +VALUE string_spec_rb_str_buf_append(VALUE self, VALUE str, VALUE two) { + return rb_str_buf_append(str, two); +} + VALUE string_spec_rb_str_buf_cat(VALUE self, VALUE str) { const char *question_mark = "?"; rb_str_buf_cat(str, question_mark, strlen(question_mark)); @@ -117,13 +124,17 @@ VALUE string_spec_rb_str_cmp(VALUE self, VALUE str1, VALUE str2) { return INT2NUM(rb_str_cmp(str1, str2)); } +VALUE string_spec_rb_str_strlen(VALUE self, VALUE str) { + return LONG2NUM(rb_str_strlen(str)); +} + VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) { rb_encoding* from_enc; rb_encoding* to_enc; from_enc = rb_to_encoding(from); - if(NIL_P(to)) { + if (NIL_P(to)) { to_enc = 0; } else { to_enc = rb_to_encoding(to); @@ -133,14 +144,13 @@ VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) { } VALUE string_spec_rb_str_conv_enc_opts(VALUE self, VALUE str, VALUE from, VALUE to, - VALUE ecflags, VALUE ecopts) -{ + VALUE ecflags, VALUE ecopts) { rb_encoding* from_enc; rb_encoding* to_enc; from_enc = rb_to_encoding(from); - if(NIL_P(to)) { + if (NIL_P(to)) { to_enc = 0; } else { to_enc = rb_to_encoding(to); @@ -194,7 +204,7 @@ VALUE string_spec_rb_str_new_offset(VALUE self, VALUE str, VALUE offset, VALUE l } VALUE string_spec_rb_str_new2(VALUE self, VALUE str) { - if(NIL_P(str)) { + if (NIL_P(str)) { return rb_str_new2(""); } else { return rb_str_new2(RSTRING_PTR(str)); @@ -210,7 +220,7 @@ VALUE string_spec_rb_str_export_to_enc(VALUE self, VALUE str, VALUE enc) { } VALUE string_spec_rb_str_new_cstr(VALUE self, VALUE str) { - if(NIL_P(str)) { + if (NIL_P(str)) { return rb_str_new_cstr(""); } else { return rb_str_new_cstr(RSTRING_PTR(str)); @@ -249,14 +259,6 @@ VALUE string_spec_rb_str_new5(VALUE self, VALUE str, VALUE ptr, VALUE len) { return rb_str_new5(str, RSTRING_PTR(ptr), FIX2INT(len)); } -VALUE string_spec_rb_tainted_str_new(VALUE self, VALUE str, VALUE len) { - return rb_tainted_str_new(RSTRING_PTR(str), FIX2INT(len)); -} - -VALUE string_spec_rb_tainted_str_new2(VALUE self, VALUE str) { - return rb_tainted_str_new2(RSTRING_PTR(str)); -} - VALUE string_spec_rb_str_plus(VALUE self, VALUE str1, VALUE str2) { return rb_str_plus(str1, str2); } @@ -279,6 +281,16 @@ VALUE string_spec_rb_str_resize_RSTRING_LEN(VALUE self, VALUE str, VALUE size) { return INT2FIX(RSTRING_LEN(modified)); } +VALUE string_spec_rb_str_resize_copy(VALUE self, VALUE str) { + rb_str_modify_expand(str, 5); + char *buffer = RSTRING_PTR(str); + buffer[1] = 'e'; + buffer[2] = 's'; + buffer[3] = 't'; + rb_str_resize(str, 4); + return str; +} + VALUE string_spec_rb_str_split(VALUE self, VALUE str) { return rb_str_split(str, ","); } @@ -291,6 +303,26 @@ VALUE string_spec_rb_str_substr(VALUE self, VALUE str, VALUE beg, VALUE len) { return rb_str_substr(str, FIX2INT(beg), FIX2INT(len)); } +VALUE string_spec_rb_str_subpos(VALUE self, VALUE str, VALUE beg) { + char* original = RSTRING_PTR(str); + char* end = RSTRING_END(str); + long len = rb_str_strlen(str); + char *p = rb_str_subpos(str, FIX2LONG(beg), &len); + if (p == NULL) { + return Qnil; + } + + if (p >= original && p <= end) { + return rb_ary_new_from_args(2, LONG2FIX(p - RSTRING_PTR(str)), LONG2FIX(len)); + } else { + rb_raise(rb_eRuntimeError, "the returned pointer is not inside the original string buffer"); + } +} + +VALUE string_spec_rb_str_sublen(VALUE self, VALUE str, VALUE pos) { + return LONG2FIX(rb_str_sublen(str, FIX2LONG(pos))); +} + VALUE string_spec_rb_str_to_str(VALUE self, VALUE arg) { return rb_str_to_str(arg); } @@ -303,6 +335,11 @@ VALUE string_spec_RSTRING_LENINT(VALUE self, VALUE str) { return INT2FIX(RSTRING_LENINT(str)); } +VALUE string_spec_RSTRING_PTR(VALUE self, VALUE str) { + char* ptr = RSTRING_PTR(str); + return PTR2NUM(ptr); +} + VALUE string_spec_RSTRING_PTR_iterate(VALUE self, VALUE str) { int i; char* ptr; @@ -354,7 +391,7 @@ VALUE string_spec_RSTRING_PTR_set(VALUE self, VALUE str, VALUE i, VALUE chr) { VALUE string_spec_RSTRING_PTR_after_funcall(VALUE self, VALUE str, VALUE cb) { /* Silence gcc 4.3.2 warning about computed value not used */ - if(RSTRING_PTR(str)) { /* force it out */ + if (RSTRING_PTR(str)) { /* force it out */ rb_funcall(cb, rb_intern("call"), 1, str); } @@ -374,6 +411,40 @@ VALUE string_spec_RSTRING_PTR_after_yield(VALUE self, VALUE str) { return from_rstring_ptr; } +VALUE string_spec_RSTRING_PTR_read(VALUE self, VALUE str, VALUE path) { + char *cpath = StringValueCStr(path); + int fd = open(cpath, O_RDONLY); + VALUE capacities = rb_ary_new(); + if (fd < 0) { + rb_syserr_fail(errno, "open"); + } + + rb_str_modify_expand(str, 30); + rb_ary_push(capacities, SIZET2NUM(rb_str_capacity(str))); + char *buffer = RSTRING_PTR(str); + if (read(fd, buffer, 30) < 0) { + rb_syserr_fail(errno, "read"); + } + rb_str_set_len(str, 30); + + rb_str_modify_expand(str, 53); + rb_ary_push(capacities, SIZET2NUM(rb_str_capacity(str))); + char *buffer2 = RSTRING_PTR(str); + if (read(fd, buffer2 + 30, 53 - 30) < 0) { + rb_syserr_fail(errno, "read"); + } + + rb_str_set_len(str, 53); + close(fd); + return capacities; +} + +VALUE string_spec_RSTRING_PTR_null_terminate(VALUE self, VALUE str, VALUE min_length) { + char* ptr = RSTRING_PTR(str); + char* end = ptr + RSTRING_LEN(str); + return rb_str_new(end, FIX2LONG(min_length)); +} + VALUE string_spec_StringValue(VALUE self, VALUE str) { return StringValue(str); } @@ -402,6 +473,7 @@ static VALUE string_spec_rb_str_free(VALUE self, VALUE str) { static VALUE string_spec_rb_sprintf1(VALUE self, VALUE str, VALUE repl) { return rb_sprintf(RSTRING_PTR(str), RSTRING_PTR(repl)); } + static VALUE string_spec_rb_sprintf2(VALUE self, VALUE str, VALUE repl1, VALUE repl2) { return rb_sprintf(RSTRING_PTR(str), RSTRING_PTR(repl1), RSTRING_PTR(repl2)); } @@ -414,6 +486,33 @@ static VALUE string_spec_rb_sprintf4(VALUE self, VALUE str) { return rb_sprintf("Result: %+" PRIsVALUE ".", str); } +static VALUE string_spec_rb_sprintf5(VALUE self, VALUE width, VALUE precision, VALUE str) { + return rb_sprintf("Result: %*.*s.", FIX2INT(width), FIX2INT(precision), RSTRING_PTR(str)); +} + +static VALUE string_spec_rb_sprintf6(VALUE self, VALUE width, VALUE precision, VALUE str) { + return rb_sprintf("Result: %*.*" PRIsVALUE ".", FIX2INT(width), FIX2INT(precision), str); +} + +static VALUE string_spec_rb_sprintf7(VALUE self, VALUE str, VALUE obj) { + VALUE results = rb_ary_new(); + rb_ary_push(results, rb_sprintf(RSTRING_PTR(str), obj)); + char cstr[256]; + int len = snprintf(cstr, 256, RSTRING_PTR(str), obj); + rb_ary_push(results, rb_str_new(cstr, len)); + return results; +} + +static VALUE string_spec_rb_sprintf8(VALUE self, VALUE str, VALUE num) { + VALUE results = rb_ary_new(); + rb_ary_push(results, rb_sprintf(RSTRING_PTR(str), FIX2LONG(num))); + char cstr[256]; + int len = snprintf(cstr, 256, RSTRING_PTR(str), FIX2LONG(num)); + rb_ary_push(results, rb_str_new(cstr, len)); + return results; +} + +PRINTF_ARGS(static VALUE string_spec_rb_vsprintf_worker(char* fmt, ...), 1, 2); static VALUE string_spec_rb_vsprintf_worker(char* fmt, ...) { va_list varargs; VALUE str; @@ -465,7 +564,10 @@ static VALUE string_spec_rb_str_modify(VALUE self, VALUE str) { } static VALUE string_spec_rb_utf8_str_new_static(VALUE self) { - return rb_utf8_str_new_static("nokogiri", 8); + const char* literal = "nokogiri"; + return rb_ary_new_from_args(2, + rb_utf8_str_new_static("nokogiri", 8), + PTR2NUM(literal)); } static VALUE string_spec_rb_utf8_str_new(VALUE self) { @@ -476,11 +578,57 @@ static VALUE string_spec_rb_utf8_str_new_cstr(VALUE self) { return rb_utf8_str_new_cstr("nokogiri"); } +PRINTF_ARGS(static VALUE call_rb_str_vcatf(VALUE mesg, const char *fmt, ...), 2, 3); +static VALUE call_rb_str_vcatf(VALUE mesg, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + VALUE result = rb_str_vcatf(mesg, fmt, ap); + va_end(ap); + return result; +} + +static VALUE string_spec_rb_str_vcatf(VALUE self, VALUE mesg) { + return call_rb_str_vcatf(mesg, "fmt %d %d number", 42, 7); +} + +static VALUE string_spec_rb_str_catf(VALUE self, VALUE mesg) { + return rb_str_catf(mesg, "fmt %d %d number", 41, 6); +} + +static VALUE string_spec_rb_str_locktmp(VALUE self, VALUE str) { + return rb_str_locktmp(str); +} + +static VALUE string_spec_rb_str_unlocktmp(VALUE self, VALUE str) { + return rb_str_unlocktmp(str); +} + +static VALUE string_spec_rb_enc_interned_str_cstr(VALUE self, VALUE str, VALUE enc) { + rb_encoding *e = NIL_P(enc) ? 0 : rb_to_encoding(enc); + return rb_enc_interned_str_cstr(RSTRING_PTR(str), e); +} + +static VALUE string_spec_rb_enc_interned_str(VALUE self, VALUE str, VALUE len, VALUE enc) { + rb_encoding *e = NIL_P(enc) ? 0 : rb_to_encoding(enc); + return rb_enc_interned_str(RSTRING_PTR(str), FIX2LONG(len), e); +} + +static VALUE string_spec_rb_str_to_interned_str(VALUE self, VALUE str) { + return rb_str_to_interned_str(str); +} + +static VALUE string_spec_rb_interned_str(VALUE self, VALUE str, VALUE len) { + return rb_interned_str(RSTRING_PTR(str), FIX2LONG(len)); +} + +static VALUE string_spec_rb_interned_str_cstr(VALUE self, VALUE str) { + return rb_interned_str_cstr(RSTRING_PTR(str)); +} + void Init_string_spec(void) { VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject); rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2); rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3); - rb_define_method(cls, "rb_fstring", string_spec_rb_str_fstring, 1); rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2); rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2); rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2); @@ -488,6 +636,7 @@ void Init_string_spec(void) { rb_define_method(cls, "rb_str_buf_new2", string_spec_rb_str_buf_new2, 0); rb_define_method(cls, "rb_str_tmp_new", string_spec_rb_str_tmp_new, 1); rb_define_method(cls, "rb_str_tmp_new_klass", string_spec_rb_str_tmp_new_klass, 1); + rb_define_method(cls, "rb_str_buf_append", string_spec_rb_str_buf_append, 2); rb_define_method(cls, "rb_str_buf_cat", string_spec_rb_str_buf_cat, 1); rb_define_method(cls, "rb_enc_str_buf_cat", string_spec_rb_enc_str_buf_cat, 3); rb_define_method(cls, "rb_str_cat", string_spec_rb_str_cat, 1); @@ -495,6 +644,7 @@ void Init_string_spec(void) { rb_define_method(cls, "rb_str_cat_cstr", string_spec_rb_str_cat_cstr, 2); rb_define_method(cls, "rb_str_cat_cstr_constant", string_spec_rb_str_cat_cstr_constant, 1); rb_define_method(cls, "rb_str_cmp", string_spec_rb_str_cmp, 2); + rb_define_method(cls, "rb_str_strlen", string_spec_rb_str_strlen, 1); rb_define_method(cls, "rb_str_conv_enc", string_spec_rb_str_conv_enc, 3); rb_define_method(cls, "rb_str_conv_enc_opts", string_spec_rb_str_conv_enc_opts, 5); rb_define_method(cls, "rb_str_drop_bytes", string_spec_rb_str_drop_bytes, 2); @@ -520,21 +670,23 @@ void Init_string_spec(void) { rb_define_method(cls, "rb_str_new3", string_spec_rb_str_new3, 1); rb_define_method(cls, "rb_str_new4", string_spec_rb_str_new4, 1); rb_define_method(cls, "rb_str_new5", string_spec_rb_str_new5, 3); - rb_define_method(cls, "rb_tainted_str_new", string_spec_rb_tainted_str_new, 2); - rb_define_method(cls, "rb_tainted_str_new2", string_spec_rb_tainted_str_new2, 1); rb_define_method(cls, "rb_str_plus", string_spec_rb_str_plus, 2); rb_define_method(cls, "rb_str_times", string_spec_rb_str_times, 2); rb_define_method(cls, "rb_str_modify_expand", string_spec_rb_str_modify_expand, 2); rb_define_method(cls, "rb_str_resize", string_spec_rb_str_resize, 2); rb_define_method(cls, "rb_str_resize_RSTRING_LEN", string_spec_rb_str_resize_RSTRING_LEN, 2); + rb_define_method(cls, "rb_str_resize_copy", string_spec_rb_str_resize_copy, 1); rb_define_method(cls, "rb_str_set_len", string_spec_rb_str_set_len, 2); rb_define_method(cls, "rb_str_set_len_RSTRING_LEN", string_spec_rb_str_set_len_RSTRING_LEN, 2); rb_define_method(cls, "rb_str_split", string_spec_rb_str_split, 1); rb_define_method(cls, "rb_str_subseq", string_spec_rb_str_subseq, 3); rb_define_method(cls, "rb_str_substr", string_spec_rb_str_substr, 3); + rb_define_method(cls, "rb_str_subpos", string_spec_rb_str_subpos, 2); + rb_define_method(cls, "rb_str_sublen", string_spec_rb_str_sublen, 2); rb_define_method(cls, "rb_str_to_str", string_spec_rb_str_to_str, 1); rb_define_method(cls, "RSTRING_LEN", string_spec_RSTRING_LEN, 1); rb_define_method(cls, "RSTRING_LENINT", string_spec_RSTRING_LENINT, 1); + rb_define_method(cls, "RSTRING_PTR", string_spec_RSTRING_PTR, 1); rb_define_method(cls, "RSTRING_PTR_iterate", string_spec_RSTRING_PTR_iterate, 1); rb_define_method(cls, "RSTRING_PTR_iterate_uint32", string_spec_RSTRING_PTR_iterate_uint32, 1); rb_define_method(cls, "RSTRING_PTR_short_memcpy", string_spec_RSTRING_PTR_short_memcpy, 1); @@ -542,6 +694,8 @@ void Init_string_spec(void) { rb_define_method(cls, "RSTRING_PTR_set", string_spec_RSTRING_PTR_set, 3); rb_define_method(cls, "RSTRING_PTR_after_funcall", string_spec_RSTRING_PTR_after_funcall, 2); rb_define_method(cls, "RSTRING_PTR_after_yield", string_spec_RSTRING_PTR_after_yield, 1); + rb_define_method(cls, "RSTRING_PTR_read", string_spec_RSTRING_PTR_read, 2); + rb_define_method(cls, "RSTRING_PTR_null_terminate", string_spec_RSTRING_PTR_null_terminate, 2); rb_define_method(cls, "StringValue", string_spec_StringValue, 1); rb_define_method(cls, "SafeStringValue", string_spec_SafeStringValue, 1); rb_define_method(cls, "rb_str_hash", string_spec_rb_str_hash, 1); @@ -551,6 +705,10 @@ void Init_string_spec(void) { rb_define_method(cls, "rb_sprintf2", string_spec_rb_sprintf2, 3); rb_define_method(cls, "rb_sprintf3", string_spec_rb_sprintf3, 1); rb_define_method(cls, "rb_sprintf4", string_spec_rb_sprintf4, 1); + rb_define_method(cls, "rb_sprintf5", string_spec_rb_sprintf5, 3); + rb_define_method(cls, "rb_sprintf6", string_spec_rb_sprintf6, 3); + rb_define_method(cls, "rb_sprintf7", string_spec_rb_sprintf7, 2); + rb_define_method(cls, "rb_sprintf8", string_spec_rb_sprintf8, 2); rb_define_method(cls, "rb_vsprintf", string_spec_rb_vsprintf, 4); rb_define_method(cls, "rb_str_equal", string_spec_rb_str_equal, 2); rb_define_method(cls, "rb_usascii_str_new", string_spec_rb_usascii_str_new, 2); @@ -563,6 +721,15 @@ void Init_string_spec(void) { rb_define_method(cls, "rb_utf8_str_new_static", string_spec_rb_utf8_str_new_static, 0); rb_define_method(cls, "rb_utf8_str_new", string_spec_rb_utf8_str_new, 0); rb_define_method(cls, "rb_utf8_str_new_cstr", string_spec_rb_utf8_str_new_cstr, 0); + rb_define_method(cls, "rb_str_vcatf", string_spec_rb_str_vcatf, 1); + rb_define_method(cls, "rb_str_catf", string_spec_rb_str_catf, 1); + rb_define_method(cls, "rb_str_locktmp", string_spec_rb_str_locktmp, 1); + rb_define_method(cls, "rb_str_unlocktmp", string_spec_rb_str_unlocktmp, 1); + rb_define_method(cls, "rb_enc_interned_str_cstr", string_spec_rb_enc_interned_str_cstr, 2); + rb_define_method(cls, "rb_enc_interned_str", string_spec_rb_enc_interned_str, 3); + rb_define_method(cls, "rb_str_to_interned_str", string_spec_rb_str_to_interned_str, 1); + rb_define_method(cls, "rb_interned_str", string_spec_rb_interned_str, 2); + rb_define_method(cls, "rb_interned_str_cstr", string_spec_rb_interned_str_cstr, 1); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/struct_spec.c b/spec/ruby/optional/capi/ext/struct_spec.c index 0393d6937d..1c669d153e 100644 --- a/spec/ruby/optional/capi/ext/struct_spec.c +++ b/spec/ruby/optional/capi/ext/struct_spec.c @@ -15,13 +15,11 @@ static VALUE struct_spec_rb_struct_getmember(VALUE self, VALUE st, VALUE key) { return rb_struct_getmember(st, SYM2ID(key)); } -static VALUE struct_spec_rb_struct_s_members(VALUE self, VALUE klass) -{ +static VALUE struct_spec_rb_struct_s_members(VALUE self, VALUE klass) { return rb_ary_dup(rb_struct_s_members(klass)); } -static VALUE struct_spec_rb_struct_members(VALUE self, VALUE st) -{ +static VALUE struct_spec_rb_struct_members(VALUE self, VALUE st) { return rb_ary_dup(rb_struct_members(st)); } @@ -30,7 +28,7 @@ static VALUE struct_spec_rb_struct_aset(VALUE self, VALUE st, VALUE key, VALUE v } /* Only allow setting three attributes, should be sufficient for testing. */ -static VALUE struct_spec_struct_define(VALUE self, VALUE name, +static VALUE struct_spec_rb_struct_define(VALUE self, VALUE name, VALUE attr1, VALUE attr2, VALUE attr3) { const char *a1 = StringValuePtr(attr1); @@ -44,7 +42,7 @@ static VALUE struct_spec_struct_define(VALUE self, VALUE name, } /* Only allow setting three attributes, should be sufficient for testing. */ -static VALUE struct_spec_struct_define_under(VALUE self, VALUE outer, +static VALUE struct_spec_rb_struct_define_under(VALUE self, VALUE outer, VALUE name, VALUE attr1, VALUE attr2, VALUE attr3) { const char *nm = StringValuePtr(name); @@ -56,17 +54,33 @@ static VALUE struct_spec_struct_define_under(VALUE self, VALUE outer, } static VALUE struct_spec_rb_struct_new(VALUE self, VALUE klass, - VALUE a, VALUE b, VALUE c) -{ - + VALUE a, VALUE b, VALUE c) { return rb_struct_new(klass, a, b, c); } -static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st) -{ +static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st) { return rb_struct_size(st); } +static VALUE struct_spec_rb_struct_initialize(VALUE self, VALUE st, VALUE values) { + return rb_struct_initialize(st, values); +} + +/* Only allow setting three attributes, should be sufficient for testing. */ +static VALUE struct_spec_rb_data_define(VALUE self, VALUE superclass, + VALUE attr1, VALUE attr2, VALUE attr3) { + + const char *a1 = StringValuePtr(attr1); + const char *a2 = StringValuePtr(attr2); + const char *a3 = StringValuePtr(attr3); + + if (superclass == Qnil) { + superclass = 0; + } + + return rb_data_define(superclass, a1, a2, a3, NULL); +} + void Init_struct_spec(void) { VALUE cls = rb_define_class("CApiStructSpecs", rb_cObject); rb_define_method(cls, "rb_struct_aref", struct_spec_rb_struct_aref, 2); @@ -74,10 +88,12 @@ void Init_struct_spec(void) { rb_define_method(cls, "rb_struct_s_members", struct_spec_rb_struct_s_members, 1); rb_define_method(cls, "rb_struct_members", struct_spec_rb_struct_members, 1); rb_define_method(cls, "rb_struct_aset", struct_spec_rb_struct_aset, 3); - rb_define_method(cls, "rb_struct_define", struct_spec_struct_define, 4); - rb_define_method(cls, "rb_struct_define_under", struct_spec_struct_define_under, 5); + rb_define_method(cls, "rb_struct_define", struct_spec_rb_struct_define, 4); + rb_define_method(cls, "rb_struct_define_under", struct_spec_rb_struct_define_under, 5); rb_define_method(cls, "rb_struct_new", struct_spec_rb_struct_new, 4); rb_define_method(cls, "rb_struct_size", struct_spec_rb_struct_size, 1); + rb_define_method(cls, "rb_struct_initialize", struct_spec_rb_struct_initialize, 2); + rb_define_method(cls, "rb_data_define", struct_spec_rb_data_define, 4); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/symbol_spec.c b/spec/ruby/optional/capi/ext/symbol_spec.c index 7d9a7b4379..ba88635faa 100644 --- a/spec/ruby/optional/capi/ext/symbol_spec.c +++ b/spec/ruby/optional/capi/ext/symbol_spec.c @@ -47,10 +47,19 @@ VALUE symbol_spec_rb_id2name(VALUE self, VALUE symbol) { return rb_str_new(c_str, strlen(c_str)); } +VALUE symbol_spec_rb_id2name_id_zero(VALUE self) { + const char* c_str = rb_id2name((ID) 0); + return c_str ? rb_str_new(c_str, strlen(c_str)) : Qnil; +} + VALUE symbol_spec_rb_id2str(VALUE self, VALUE symbol) { return rb_id2str(SYM2ID(symbol)); } +VALUE symbol_spec_rb_id2str_id_zero(VALUE self) { + return rb_id2str((ID) 0); +} + VALUE symbol_spec_rb_intern_str(VALUE self, VALUE str) { return ID2SYM(rb_intern_str(str)); } @@ -90,7 +99,9 @@ void Init_symbol_spec(void) { rb_define_method(cls, "rb_intern3", symbol_spec_rb_intern3, 3); rb_define_method(cls, "rb_intern3_c_compare", symbol_spec_rb_intern3_c_compare, 4); rb_define_method(cls, "rb_id2name", symbol_spec_rb_id2name, 1); + rb_define_method(cls, "rb_id2name_id_zero", symbol_spec_rb_id2name_id_zero, 0); rb_define_method(cls, "rb_id2str", symbol_spec_rb_id2str, 1); + rb_define_method(cls, "rb_id2str_id_zero", symbol_spec_rb_id2str_id_zero, 0); rb_define_method(cls, "rb_intern_str", symbol_spec_rb_intern_str, 1); rb_define_method(cls, "rb_check_symbol_cstr", symbol_spec_rb_check_symbol_cstr, 1); rb_define_method(cls, "rb_is_class_id", symbol_spec_rb_is_class_id, 1); diff --git a/spec/ruby/optional/capi/ext/thread_spec.c b/spec/ruby/optional/capi/ext/thread_spec.c index 21f98dec52..ac77e4e813 100644 --- a/spec/ruby/optional/capi/ext/thread_spec.c +++ b/spec/ruby/optional/capi/ext/thread_spec.c @@ -8,7 +8,14 @@ #include <unistd.h> #endif #if defined(_WIN32) -#define pipe(p) rb_w32_pipe(p) +#include "ruby/win32.h" +#define read rb_w32_read +#define write rb_w32_write +#define pipe rb_w32_pipe +#endif + +#ifndef _WIN32 +#include <pthread.h> #endif #ifdef __cplusplus @@ -19,10 +26,6 @@ static VALUE thread_spec_rb_thread_alone(VALUE self) { return rb_thread_alone() ? Qtrue : Qfalse; } -#if defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - /* This is unblocked by unblock_func(). */ static void* blocking_gvl_func(void* data) { int rfd = *(int *)data; @@ -64,7 +67,7 @@ static VALUE thread_spec_rb_thread_call_without_gvl(VALUE self) { } /* This is unblocked by a signal. */ -static void* blocking_gvl_func_for_udf_io(void *data) { +static void* blocking_gvl_func_for_ubf_io(void *data) { int rfd = (int)(size_t)data; char dummy; @@ -84,7 +87,7 @@ static VALUE thread_spec_rb_thread_call_without_gvl_with_ubf_io(VALUE self) { rb_raise(rb_eRuntimeError, "could not create pipe"); } - ret = rb_thread_call_without_gvl(blocking_gvl_func_for_udf_io, + ret = rb_thread_call_without_gvl(blocking_gvl_func_for_ubf_io, (void*)(size_t)fds[0], RUBY_UBF_IO, 0); close(fds[0]); close(fds[1]); @@ -115,7 +118,6 @@ static VALUE thread_spec_rb_thread_wait_for(VALUE self, VALUE s, VALUE ms) { return Qnil; } - VALUE thread_spec_call_proc(void *arg_ptr) { VALUE arg_array = (VALUE)arg_ptr; VALUE arg = rb_ary_pop(arg_array); @@ -131,6 +133,44 @@ static VALUE thread_spec_rb_thread_create(VALUE self, VALUE proc, VALUE arg) { return rb_thread_create(thread_spec_call_proc, (void*)args); } +static VALUE thread_spec_ruby_native_thread_p(VALUE self) { + if (ruby_native_thread_p()) { + return Qtrue; + } else { + return Qfalse; + } +} + +#ifndef _WIN32 +static VALUE false_result = Qfalse; +static VALUE true_result = Qtrue; + +static void *new_thread_check(void *args) { + if (ruby_native_thread_p()) { + return &true_result; + } else { + return &false_result; + } +} +#endif + +static VALUE thread_spec_ruby_native_thread_p_new_thread(VALUE self) { +#ifndef _WIN32 + pthread_t t; + void *result = &true_result; + pthread_create(&t, NULL, new_thread_check, NULL); + pthread_join(t, &result); + return *(VALUE *)result; +#else + return Qfalse; +#endif +} + +#ifdef RUBY_VERSION_IS_4_0 +static VALUE thread_spec_ruby_thread_has_gvl_p(VALUE self) { + return ruby_thread_has_gvl_p() ? Qtrue : Qfalse; +} +#endif void Init_thread_spec(void) { VALUE cls = rb_define_class("CApiThreadSpecs", rb_cObject); @@ -143,6 +183,11 @@ void Init_thread_spec(void) { rb_define_method(cls, "rb_thread_wakeup", thread_spec_rb_thread_wakeup, 1); rb_define_method(cls, "rb_thread_wait_for", thread_spec_rb_thread_wait_for, 2); rb_define_method(cls, "rb_thread_create", thread_spec_rb_thread_create, 2); + rb_define_method(cls, "ruby_native_thread_p", thread_spec_ruby_native_thread_p, 0); + rb_define_method(cls, "ruby_native_thread_p_new_thread", thread_spec_ruby_native_thread_p_new_thread, 0); +#ifdef RUBY_VERSION_IS_4_0 + rb_define_method(cls, "ruby_thread_has_gvl_p", thread_spec_ruby_thread_has_gvl_p, 0); +#endif } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/tracepoint_spec.c b/spec/ruby/optional/capi/ext/tracepoint_spec.c index 78c459d6cb..6666c8f85c 100644 --- a/spec/ruby/optional/capi/ext/tracepoint_spec.c +++ b/spec/ruby/optional/capi/ext/tracepoint_spec.c @@ -17,7 +17,7 @@ static VALUE tracepoint_spec_rb_tracepoint_new(VALUE self, VALUE data) { return rb_tracepoint_new(Qnil, RUBY_EVENT_LINE, callback, (void*) data); } -static VALUE tracepoint_spec_callback_called(VALUE self){ +static VALUE tracepoint_spec_callback_called(VALUE self) { return callback_called; } diff --git a/spec/ruby/optional/capi/ext/typed_data_spec.c b/spec/ruby/optional/capi/ext/typed_data_spec.c index 70f21ce36f..c6fcfa3bc8 100644 --- a/spec/ruby/optional/capi/ext/typed_data_spec.c +++ b/spec/ruby/optional/capi/ext/typed_data_spec.c @@ -106,6 +106,16 @@ VALUE sws_typed_wrap_struct(VALUE self, VALUE val) { return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar); } +#ifndef RUBY_VERSION_IS_4_1 +#undef RUBY_UNTYPED_DATA_WARNING +#define RUBY_UNTYPED_DATA_WARNING 0 +VALUE sws_untyped_wrap_struct(VALUE self, VALUE val) { + int* data = (int*) malloc(sizeof(int)); + *data = FIX2INT(val); + return Data_Wrap_Struct(rb_cObject, NULL, free, data); +} +#endif + VALUE sws_typed_get_struct(VALUE self, VALUE obj) { struct sample_typed_wrapped_struct* bar; TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct, &sample_typed_wrapped_struct_data_type, bar); @@ -148,6 +158,11 @@ VALUE sws_typed_change_struct(VALUE self, VALUE obj, VALUE new_val) { return Qnil; } +VALUE sws_typed_rb_check_type(VALUE self, VALUE obj, VALUE other) { + rb_check_type(obj, TYPE(other)); + return Qtrue; +} + VALUE sws_typed_rb_check_typeddata_same_type(VALUE self, VALUE obj) { return rb_check_typeddata(obj, &sample_typed_wrapped_struct_data_type) == DATA_PTR(obj) ? Qtrue : Qfalse; } @@ -160,24 +175,36 @@ VALUE sws_typed_rb_check_typeddata_different_type(VALUE self, VALUE obj) { return rb_check_typeddata(obj, &sample_typed_wrapped_struct_other_data_type) == DATA_PTR(obj) ? Qtrue : Qfalse; } +#ifndef RUBY_VERSION_IS_4_1 +VALUE sws_typed_RTYPEDDATA_P(VALUE self, VALUE obj) { + return RTYPEDDATA_P(obj) ? Qtrue : Qfalse; +} +#endif + void Init_typed_data_spec(void) { VALUE cls = rb_define_class("CApiAllocTypedSpecs", rb_cObject); rb_define_alloc_func(cls, sdaf_alloc_typed_func); rb_define_method(cls, "typed_wrapped_data", sdaf_typed_get_struct, 0); cls = rb_define_class("CApiWrappedTypedStructSpecs", rb_cObject); rb_define_method(cls, "typed_wrap_struct", sws_typed_wrap_struct, 1); +#ifndef RUBY_VERSION_IS_4_1 + rb_define_method(cls, "untyped_wrap_struct", sws_untyped_wrap_struct, 1); +#endif rb_define_method(cls, "typed_get_struct", sws_typed_get_struct, 1); rb_define_method(cls, "typed_get_struct_other", sws_typed_get_struct_different_type, 1); rb_define_method(cls, "typed_get_struct_parent", sws_typed_get_struct_parent_type, 1); rb_define_method(cls, "typed_get_struct_rdata", sws_typed_get_struct_rdata, 1); rb_define_method(cls, "typed_get_struct_data_ptr", sws_typed_get_struct_data_ptr, 1); rb_define_method(cls, "typed_change_struct", sws_typed_change_struct, 2); + rb_define_method(cls, "rb_check_type", sws_typed_rb_check_type, 2); rb_define_method(cls, "rb_check_typeddata_same_type", sws_typed_rb_check_typeddata_same_type, 1); rb_define_method(cls, "rb_check_typeddata_same_type_parent", sws_typed_rb_check_typeddata_same_type_parent, 1); rb_define_method(cls, "rb_check_typeddata_different_type", sws_typed_rb_check_typeddata_different_type, 1); +#ifndef RUBY_VERSION_IS_4_1 + rb_define_method(cls, "RTYPEDDATA_P", sws_typed_RTYPEDDATA_P, 1); +#endif } #ifdef __cplusplus } #endif - diff --git a/spec/ruby/optional/capi/ext/util_spec.c b/spec/ruby/optional/capi/ext/util_spec.c index a7269353c2..043da99ace 100644 --- a/spec/ruby/optional/capi/ext/util_spec.c +++ b/spec/ruby/optional/capi/ext/util_spec.c @@ -7,25 +7,24 @@ extern "C" { #endif VALUE util_spec_rb_scan_args(VALUE self, VALUE argv, VALUE fmt, VALUE expected, VALUE acc) { - int i, result, argc = (int)RARRAY_LEN(argv); - VALUE args[6], failed, a1, a2, a3, a4, a5, a6; + int result, argc; + VALUE a1, a2, a3, a4, a5, a6; - failed = rb_intern("failed"); - a1 = a2 = a3 = a4 = a5 = a6 = failed; + argc = (int) RARRAY_LEN(argv); + VALUE* args = RARRAY_PTR(argv); + /* the line above can be replaced with this for Ruby implementations which do not support RARRAY_PTR() yet + VALUE args[6]; + for(int i = 0; i < argc; i++) { + args[i] = rb_ary_entry(argv, i); + } */ - for(i = 0; i < argc; i++) { - args[i] = rb_ary_entry(argv, i); - } + a1 = a2 = a3 = a4 = a5 = a6 = INT2FIX(-1); -#ifdef RB_SCAN_ARGS_KEYWORDS if (*RSTRING_PTR(fmt) == 'k') { result = rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS, argc, args, RSTRING_PTR(fmt)+1, &a1, &a2, &a3, &a4, &a5, &a6); } else { -#endif result = rb_scan_args(argc, args, RSTRING_PTR(fmt), &a1, &a2, &a3, &a4, &a5, &a6); -#ifdef RB_SCAN_ARGS_KEYWORDS } -#endif switch(NUM2INT(expected)) { case 6: @@ -59,22 +58,17 @@ static VALUE util_spec_rb_get_kwargs(VALUE self, VALUE keyword_hash, VALUE keys, int len = RARRAY_LENINT(keys); int values_len = req + (opt < 0 ? -1 - opt : opt); - int i = 0; - ID *ids = (ID*) malloc(sizeof(VALUE) * len); - VALUE *results = (VALUE*) malloc(sizeof(VALUE) * values_len); - int extracted = 0; - VALUE ary = Qundef; + ID *ids = (ID *)alloca(sizeof(VALUE) * len); + VALUE *results = (VALUE *)alloca(sizeof(VALUE) * values_len); - for (i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { ids[i] = SYM2ID(rb_ary_entry(keys, i)); } - extracted = rb_get_kwargs(keyword_hash, ids, req, opt, results); - ary = rb_ary_new_from_values(extracted, results); - free(results); - free(ids); - return ary; + int extracted = rb_get_kwargs(keyword_hash, ids, req, opt, results); + + return rb_ary_new_from_values(extracted, results); } static VALUE util_spec_rb_long2int(VALUE self, VALUE n) { diff --git a/spec/ruby/optional/capi/fiber_spec.rb b/spec/ruby/optional/capi/fiber_spec.rb new file mode 100644 index 0000000000..c820ba17c2 --- /dev/null +++ b/spec/ruby/optional/capi/fiber_spec.rb @@ -0,0 +1,86 @@ +require_relative 'spec_helper' + +load_extension('fiber') + +describe "C-API Fiber function" do + before :each do + @s = CApiFiberSpecs.new + end + + describe "rb_fiber_current" do + it "returns the current fiber" do + result = @s.rb_fiber_current() + result.should.instance_of?(Fiber) + result.should == Fiber.current + end + end + + describe "rb_fiber_alive_p" do + it "returns the fibers alive status" do + fiber = Fiber.new { Fiber.yield } + fiber.resume + @s.rb_fiber_alive_p(fiber).should == true + fiber.resume + @s.rb_fiber_alive_p(fiber).should == false + end + end + + describe "rb_fiber_resume" do + it "resumes the fiber" do + fiber = Fiber.new { |arg| Fiber.yield arg } + @s.rb_fiber_resume(fiber, [1]).should == 1 + @s.rb_fiber_resume(fiber, [2]).should == 2 + end + end + + describe "rb_fiber_yield" do + it "yields the fiber" do + fiber = Fiber.new { @s.rb_fiber_yield([1]) } + fiber.resume.should == 1 + end + end + + describe "rb_fiber_new" do + it "returns a new fiber" do + fiber = @s.rb_fiber_new + fiber.should.instance_of?(Fiber) + fiber.resume(42).should == "42" + end + end + + describe "rb_fiber_raise" do + it "raises an exception on the resumed fiber" do + fiber = Fiber.new do + begin + Fiber.yield + rescue => error + error + end + end + + fiber.resume + + result = @s.rb_fiber_raise(fiber, "Boom!") + result.should.instance_of?(RuntimeError) + result.message.should == "Boom!" + end + + it "raises an exception on the transferred fiber" do + main = Fiber.current + + fiber = Fiber.new do + begin + main.transfer + rescue => error + error + end + end + + fiber.transfer + + result = @s.rb_fiber_raise(fiber, "Boom!") + result.should.instance_of?(RuntimeError) + result.message.should == "Boom!" + end + end +end diff --git a/spec/ruby/optional/capi/file_spec.rb b/spec/ruby/optional/capi/file_spec.rb index 96d731e4fa..fd63aed316 100644 --- a/spec/ruby/optional/capi/file_spec.rb +++ b/spec/ruby/optional/capi/file_spec.rb @@ -5,13 +5,13 @@ load_extension('file') describe :rb_file_open, shared: true do it "raises an ArgumentError if passed an empty mode string" do touch @name - -> { @s.rb_file_open(@name, "") }.should raise_error(ArgumentError) + -> { @s.rb_file_open(@name, "") }.should.raise(ArgumentError) end it "opens a file in read-only mode with 'r'" do touch(@name) { |f| f.puts "readable" } @file = @s.send(@method, @name, "r") - @file.should be_an_instance_of(File) + @file.should.instance_of?(File) @file.read.chomp.should == "readable" end @@ -65,13 +65,13 @@ describe "C-API File function" do describe "FilePathValue" do it "returns a String argument unchanged" do obj = "path" - @s.FilePathValue(obj).should eql(obj) + @s.FilePathValue(obj).should.eql?(obj) end it "does not call #to_str on a String" do - obj = "path" + obj = +"path" obj.should_not_receive(:to_str) - @s.FilePathValue(obj).should eql(obj) + @s.FilePathValue(obj).should.eql?(obj) end it "calls #to_path to convert an object to a String" do diff --git a/spec/ruby/optional/capi/finalizer_spec.rb b/spec/ruby/optional/capi/finalizer_spec.rb new file mode 100644 index 0000000000..162e8ea693 --- /dev/null +++ b/spec/ruby/optional/capi/finalizer_spec.rb @@ -0,0 +1,40 @@ +require_relative "spec_helper" + +extension_path = load_extension("finalizer") + +describe "CApiFinalizerSpecs" do + before :each do + @s = CApiFinalizerSpecs.new + end + + describe "rb_define_finalizer" do + it "defines a finalizer on the object" do + code = <<~RUBY + require #{extension_path.dump} + + obj = Object.new + finalizer = Proc.new { puts "finalizer run" } + CApiFinalizerSpecs.new.rb_define_finalizer(obj, finalizer) + RUBY + + ruby_exe(code).should == "finalizer run\n" + end + end + + describe "rb_undefine_finalizer" do + ruby_bug "#20981", "3.4.0"..."3.4.2" do + it "removes finalizers from the object" do + code = <<~RUBY + require #{extension_path.dump} + + obj = Object.new + finalizer = Proc.new { puts "finalizer run" } + ObjectSpace.define_finalizer(obj, finalizer) + CApiFinalizerSpecs.new.rb_undefine_finalizer(obj) + RUBY + + ruby_exe(code).should.empty? + end + end + end +end diff --git a/spec/ruby/optional/capi/fixnum_spec.rb b/spec/ruby/optional/capi/fixnum_spec.rb index aa02a0543b..73751c4895 100644 --- a/spec/ruby/optional/capi/fixnum_spec.rb +++ b/spec/ruby/optional/capi/fixnum_spec.rb @@ -25,9 +25,9 @@ describe "CApiFixnumSpecs" do end end - platform_is wordsize: 64 do # sizeof(long) > sizeof(int) + platform_is c_long_size: 64 do # sizeof(long) > sizeof(int) it "raises a TypeError if passed nil" do - -> { @s.FIX2INT(nil) }.should raise_error(TypeError) + -> { @s.FIX2INT(nil) }.should.raise(TypeError) end it "converts a Float" do @@ -39,16 +39,16 @@ describe "CApiFixnumSpecs" do end it "raises a RangeError if the value does not fit a native int" do - -> { @s.FIX2INT(0x7fff_ffff+1) }.should raise_error(RangeError) - -> { @s.FIX2INT(-(1 << 31) - 1) }.should raise_error(RangeError) + -> { @s.FIX2INT(0x7fff_ffff+1) }.should.raise(RangeError) + -> { @s.FIX2INT(-(1 << 31) - 1) }.should.raise(RangeError) end it "raises a RangeError if the value is more than 32bits" do - -> { @s.FIX2INT(0xffff_ffff+1) }.should raise_error(RangeError) + -> { @s.FIX2INT(0xffff_ffff+1) }.should.raise(RangeError) end it "raises a RangeError if the value is more than 64bits" do - -> { @s.FIX2INT(0xffff_ffff_ffff_ffff+1) }.should raise_error(RangeError) + -> { @s.FIX2INT(0xffff_ffff_ffff_ffff+1) }.should.raise(RangeError) end it "calls #to_int to coerce the value" do @@ -74,9 +74,9 @@ describe "CApiFixnumSpecs" do end end - platform_is wordsize: 64 do # sizeof(long) > sizeof(int) + platform_is c_long_size: 64 do # sizeof(long) > sizeof(int) it "raises a TypeError if passed nil" do - -> { @s.FIX2UINT(nil) }.should raise_error(TypeError) + -> { @s.FIX2UINT(nil) }.should.raise(TypeError) end it "converts a Float" do @@ -85,16 +85,16 @@ describe "CApiFixnumSpecs" do it "raises a RangeError if the value does not fit a native uint" do # Interestingly, on MRI FIX2UINT(-1) is allowed - -> { @s.FIX2UINT(0xffff_ffff+1) }.should raise_error(RangeError) - -> { @s.FIX2UINT(-(1 << 31) - 1) }.should raise_error(RangeError) + -> { @s.FIX2UINT(0xffff_ffff+1) }.should.raise(RangeError) + -> { @s.FIX2UINT(-(1 << 31) - 1) }.should.raise(RangeError) end it "raises a RangeError if the value is more than 32bits" do - -> { @s.FIX2UINT(0xffff_ffff+1) }.should raise_error(RangeError) + -> { @s.FIX2UINT(0xffff_ffff+1) }.should.raise(RangeError) end it "raises a RangeError if the value is more than 64bits" do - -> { @s.FIX2UINT(0xffff_ffff_ffff_ffff+1) }.should raise_error(RangeError) + -> { @s.FIX2UINT(0xffff_ffff_ffff_ffff+1) }.should.raise(RangeError) end end end diff --git a/spec/ruby/optional/capi/fixtures/class.rb b/spec/ruby/optional/capi/fixtures/class.rb index dbb0b69967..b463e3b4c3 100644 --- a/spec/ruby/optional/capi/fixtures/class.rb +++ b/spec/ruby/optional/capi/fixtures/class.rb @@ -15,6 +15,16 @@ class CApiClassSpecs end end + class KeywordAlloc + attr_reader :initialized, :args, :kwargs + + def initialize(*args, **kwargs) + @initialized = true + @args = args + @kwargs = kwargs + end + end + class Attr def initialize @foo, @bar, @baz = 1, 2, 3 @@ -87,5 +97,8 @@ class CApiClassSpecs class B end + + module M + end end end diff --git a/spec/ruby/optional/capi/fixtures/kernel.rb b/spec/ruby/optional/capi/fixtures/kernel.rb new file mode 100644 index 0000000000..d3fc7c57e8 --- /dev/null +++ b/spec/ruby/optional/capi/fixtures/kernel.rb @@ -0,0 +1,19 @@ +class CApiKernelSpecs + class ClassWithPublicMethod + def public_method(*, **) + :public + end + end + + class ClassWithPrivateMethod + private def private_method(*, **) + :private + end + end + + class ClassWithProtectedMethod + protected def protected_method(*, **) + :protected + end + end +end diff --git a/spec/ruby/optional/capi/fixtures/module.rb b/spec/ruby/optional/capi/fixtures/module.rb index ba90eb7181..aac8bfbfb3 100644 --- a/spec/ruby/optional/capi/fixtures/module.rb +++ b/spec/ruby/optional/capi/fixtures/module.rb @@ -13,6 +13,10 @@ class CApiModuleSpecs autoload :D, File.expand_path('../const_get.rb', __FILE__) X = 1 + Q = 1 + R = 2 + S = 3 + T = 5 end class B < A diff --git a/spec/ruby/optional/capi/fixtures/object.rb b/spec/ruby/optional/capi/fixtures/object.rb new file mode 100644 index 0000000000..a59f2309d8 --- /dev/null +++ b/spec/ruby/optional/capi/fixtures/object.rb @@ -0,0 +1,29 @@ +class CApiObjectSpecs + class IVars + def initialize + @a = 3 + @b = 7 + @c = 4 + end + + def self.set_class_variables + @@foo = :a + @@bar = :b + @@baz = :c + end + end + + module MVars + @@mvar = :foo + @@mvar2 = :bar + + @ivar = :baz + end + + module CVars + @@cvar = :foo + @@cvar2 = :bar + + @ivar = :baz + end +end diff --git a/spec/ruby/optional/capi/fixtures/read.txt b/spec/ruby/optional/capi/fixtures/read.txt new file mode 100644 index 0000000000..f7065a35d0 --- /dev/null +++ b/spec/ruby/optional/capi/fixtures/read.txt @@ -0,0 +1 @@ +fixture file contents to test read() with RSTRING_PTR diff --git a/spec/ruby/optional/capi/float_spec.rb b/spec/ruby/optional/capi/float_spec.rb index 4b98902b59..5b02483b28 100644 --- a/spec/ruby/optional/capi/float_spec.rb +++ b/spec/ruby/optional/capi/float_spec.rb @@ -23,8 +23,8 @@ describe "CApiFloatSpecs" do describe "rb_Float" do it "creates a new Float from a String" do f = @f.rb_Float("101.99") - f.should be_kind_of(Float) - f.should eql(101.99) + f.should.is_a?(Float) + f.should.eql?(101.99) end end diff --git a/spec/ruby/optional/capi/gc_spec.rb b/spec/ruby/optional/capi/gc_spec.rb index 23e2b7c9ab..6695026c6f 100644 --- a/spec/ruby/optional/capi/gc_spec.rb +++ b/spec/ruby/optional/capi/gc_spec.rb @@ -7,37 +7,82 @@ describe "CApiGCSpecs" do @f = CApiGCSpecs.new end - it "correctly gets the value from a registered address" do - @f.registered_tagged_address.should == 10 - @f.registered_tagged_address.should equal(@f.registered_tagged_address) - @f.registered_reference_address.should == "Globally registered data" - @f.registered_reference_address.should equal(@f.registered_reference_address) + describe "rb_gc_register_address" do + it "correctly gets the value from a registered address" do + @f.registered_tagged_address.should == 10 + @f.registered_tagged_address.should.equal?(@f.registered_tagged_address) + @f.registered_reference_address.should == "Globally registered data" + @f.registered_reference_address.should.equal?(@f.registered_reference_address) + end + + it "keeps the value alive even if the value is assigned after rb_gc_register_address() is called" do + GC.start + @f.registered_before_rb_gc_register_address.should == "registered before rb_gc_register_address()" + end + + it "can be called outside Init_" do + @f.rb_gc_register_address.should == "rb_gc_register_address() outside Init_" + @f.rb_gc_unregister_address + end end - describe "rb_gc_enable" do + describe "rb_global_variable" do + before :all do + GC.start + end + + describe "keeps the value alive even if the value is assigned after rb_global_variable() is called" do + it "for a string" do + @f.registered_before_rb_global_variable_string.should == "registered before rb_global_variable()" + end + it "for a bignum" do + @f.registered_before_rb_global_variable_bignum.should == 2**63 - 1 + end + + it "for a Float" do + @f.registered_before_rb_global_variable_float.should == 3.14 + end + end + + describe "keeps the value alive when the value is assigned before rb_global_variable() is called" do + it "for a string" do + @f.registered_after_rb_global_variable_string.should == "registered after rb_global_variable()" + end + + it "for a bignum" do + @f.registered_after_rb_global_variable_bignum.should == 2**63 - 1 + end + + it "for a Float" do + @f.registered_after_rb_global_variable_float.should == 6.28 + end + end + end + + describe "rb_gc_enable" do after do GC.enable end it "enables GC when disabled" do GC.disable - @f.rb_gc_enable.should be_true + @f.rb_gc_enable.should == true end it "GC stays enabled when enabled" do GC.enable - @f.rb_gc_enable.should be_false + @f.rb_gc_enable.should == false end it "disables GC when enabled" do GC.enable - @f.rb_gc_disable.should be_false + @f.rb_gc_disable.should == false end it "GC stays disabled when disabled" do GC.disable - @f.rb_gc_disable.should be_true + @f.rb_gc_disable.should == true end end @@ -55,23 +100,27 @@ describe "CApiGCSpecs" do -> { @f.rb_gc_adjust_memory_usage(8) @f.rb_gc_adjust_memory_usage(-8) - }.should_not raise_error + }.should_not.raise end end describe "rb_gc_register_mark_object" do it "can be called with an object" do - @f.rb_gc_register_mark_object(Object.new).should be_nil + @f.rb_gc_register_mark_object(Object.new).should == nil + end + + it "keeps the value alive even if the value is not referenced by any Ruby object" do + @f.rb_gc_register_mark_object_not_referenced_float.should == 1.61 end end describe "rb_gc_latest_gc_info" do it "raises a TypeError when hash or symbol not given" do - -> { @f.rb_gc_latest_gc_info("foo") }.should raise_error(TypeError) + -> { @f.rb_gc_latest_gc_info("foo") }.should.raise(TypeError) end it "raises an ArgumentError when unknown symbol given" do - -> { @f.rb_gc_latest_gc_info(:unknown) }.should raise_error(ArgumentError) + -> { @f.rb_gc_latest_gc_info(:unknown) }.should.raise(ArgumentError) end it "returns the populated hash when a hash is given" do @@ -81,7 +130,7 @@ describe "CApiGCSpecs" do end it "returns a value when symbol is given" do - @f.rb_gc_latest_gc_info(:state).should be_kind_of(Symbol) + @f.rb_gc_latest_gc_info(:state).should.is_a?(Symbol) end end end diff --git a/spec/ruby/optional/capi/globals_spec.rb b/spec/ruby/optional/capi/globals_spec.rb index cc6f6ef3a8..03316c256c 100644 --- a/spec/ruby/optional/capi/globals_spec.rb +++ b/spec/ruby/optional/capi/globals_spec.rb @@ -9,7 +9,7 @@ describe "CApiGlobalSpecs" do end it "correctly gets global values" do - @f.sb_gv_get("$BLAH").should == nil + suppress_warning { @f.sb_gv_get("$BLAH") }.should == nil @f.sb_gv_get("$\\").should == nil @f.sb_gv_get("\\").should == nil # rb_gv_get should change \ to $\ end @@ -21,7 +21,7 @@ describe "CApiGlobalSpecs" do end it "correctly sets global values" do - @f.sb_gv_get("$BLAH").should == nil + suppress_warning { @f.sb_gv_get("$BLAH") }.should == nil @f.sb_gv_set("$BLAH", 10) begin @f.sb_gv_get("$BLAH").should == 10 @@ -41,10 +41,19 @@ describe "CApiGlobalSpecs" do @f.sb_get_global_value.should == "XYZ" end + run = 0 + it "rb_define_readonly_variable should define a new readonly global variable" do - @f.rb_define_readonly_variable("ro_gvar", 15) - $ro_gvar.should == 15 - -> { $ro_gvar = 10 }.should raise_error(NameError) + name = "ro_gvar#{run += 1}" + eval <<~RUBY + # Check the gvar doesn't exist and ensure rb_gv_get doesn't implicitly declare the gvar, + # otherwise the rb_define_readonly_variable call will conflict. + suppress_warning { @f.sb_gv_get("#{name}") }.should == nil + + @f.rb_define_readonly_variable("#{name}", 15) + $#{name}.should == 15 + -> { $#{name} = 10 }.should.raise(NameError) + RUBY end it "rb_define_hooked_variable should define a C hooked global variable" do @@ -53,6 +62,52 @@ describe "CApiGlobalSpecs" do $hooked_gvar.should == 4 end + it "rb_define_hooked_variable should use default accessors if NULL ones are supplied" do + @f.rb_define_hooked_variable_default_accessors("$hooked_gvar_default_accessors") + $hooked_gvar_default_accessors = 10 + $hooked_gvar_default_accessors.should == 10 + end + + it "rb_define_hooked_variable with default accessors should return nil for NULL variables" do + @f.rb_define_hooked_variable_null_var("$hooked_gvar_null_value") + $hooked_gvar_null_value.should == nil + end + + describe "rb_define_virtual_variable" do + describe "with default accessors" do + before :all do + @f.rb_define_virtual_variable_default_accessors("$virtual_variable_default_accessors") + end + + it "is read-only" do + -> { $virtual_variable_default_accessors = 10 }.should.raise(NameError, /read-only/) + end + + it "returns false with the default getter" do + $virtual_variable_default_accessors.should == false + $virtual_variable_default_accessors.should == false + end + end + + describe "with supplied accessors" do + before :all do + @f.rb_define_virtual_variable_incrementing_accessors("$virtual_variable_incrementing_accessors") + end + + it "returns a dynamically changing value" do + $virtual_variable_incrementing_accessors = 20 + $virtual_variable_incrementing_accessors.should == 20 + $virtual_variable_incrementing_accessors.should == 21 + $virtual_variable_incrementing_accessors.should == 22 + + $virtual_variable_incrementing_accessors = 100 + $virtual_variable_incrementing_accessors.should == 100 + $virtual_variable_incrementing_accessors.should == 101 + $virtual_variable_incrementing_accessors.should == 102 + end + end + end + describe "rb_fs" do before :each do @field_separator = $; @@ -109,7 +164,7 @@ describe "CApiGlobalSpecs" do it "returns $stdin" do $stdin = @stream - @f.rb_stdin.should equal($stdin) + @f.rb_stdin.should.equal?($stdin) end end @@ -120,7 +175,7 @@ describe "CApiGlobalSpecs" do it "returns $stdout" do $stdout = @stream - @f.rb_stdout.should equal($stdout) + @f.rb_stdout.should.equal?($stdout) end end @@ -131,7 +186,7 @@ describe "CApiGlobalSpecs" do it "returns $stderr" do $stderr = @stream - @f.rb_stderr.should equal($stderr) + @f.rb_stderr.should.equal?($stderr) end end @@ -142,7 +197,7 @@ describe "CApiGlobalSpecs" do it "is an alias of rb_stdout" do $stdout = @stream - @f.rb_defout.should equal($stdout) + @f.rb_defout.should.equal?($stdout) end end end @@ -163,7 +218,7 @@ describe "CApiGlobalSpecs" do end it "returns nil by default" do - @f.rb_output_rs.should be_nil + @f.rb_output_rs.should == nil end it "returns the value of $\\" do @@ -182,7 +237,7 @@ describe "CApiGlobalSpecs" do end it "returns nil by default" do - @f.rb_output_fs.should be_nil + @f.rb_output_fs.should == nil end it "returns the value of $\\" do @@ -208,7 +263,7 @@ describe "CApiGlobalSpecs" do end Thread.pass while thr.status and !running - $_.should be_nil + $_.should == nil thr.join end @@ -235,7 +290,7 @@ describe "CApiGlobalSpecs" do end Thread.pass while thr.status and !running - $_.should be_nil + $_.should == nil thr.join end diff --git a/spec/ruby/optional/capi/hash_spec.rb b/spec/ruby/optional/capi/hash_spec.rb index a60467a66b..842bc82b97 100644 --- a/spec/ruby/optional/capi/hash_spec.rb +++ b/spec/ruby/optional/capi/hash_spec.rb @@ -36,7 +36,7 @@ describe "C-API Hash function" do obj = mock("rb_hash no to_int") obj.should_receive(:hash).and_return(nil) - -> { @s.rb_hash(obj) }.should raise_error(TypeError) + -> { @s.rb_hash(obj) }.should.raise(TypeError) end end @@ -46,7 +46,21 @@ describe "C-API Hash function" do end it "creates a hash with no default proc" do - @s.rb_hash_new {}.default_proc.should be_nil + @s.rb_hash_new {}.default_proc.should == nil + end + end + + describe "rb_hash_new_capa" do + it "returns a new hash" do + @s.rb_hash_new_capa(3).should == {} + end + + it "creates a hash with no default proc" do + @s.rb_hash_new_capa(3) {}.default_proc.should == nil + end + + it "raises RuntimeError when negative index is provided" do + -> { @s.rb_hash_new_capa(-1) }.should.raise(RuntimeError, "st_table too big") end end @@ -63,13 +77,13 @@ describe "C-API Hash function" do hsh = {} dup = @s.rb_hash_dup(hsh) dup.should == hsh - dup.should_not equal(hsh) + dup.should_not.equal?(hsh) end end describe "rb_hash_freeze" do it "freezes the hash" do - @s.rb_hash_freeze({}).frozen?.should be_true + @s.rb_hash_freeze({}).frozen?.should == true end end @@ -82,13 +96,13 @@ describe "C-API Hash function" do it "returns the default value if it exists" do hsh = Hash.new(0) @s.rb_hash_aref(hsh, :chunky).should == 0 - @s.rb_hash_aref_nil(hsh, :chunky).should be_false + @s.rb_hash_aref_nil(hsh, :chunky).should == false end it "returns nil if the key does not exist" do hsh = { } - @s.rb_hash_aref(hsh, :chunky).should be_nil - @s.rb_hash_aref_nil(hsh, :chunky).should be_true + @s.rb_hash_aref(hsh, :chunky).should == nil + @s.rb_hash_aref_nil(hsh, :chunky).should == true end end @@ -103,7 +117,7 @@ describe "C-API Hash function" do describe "rb_hash_clear" do it "returns self that cleared keys and values" do hsh = { :key => 'value' } - @s.rb_hash_clear(hsh).should equal(hsh) + @s.rb_hash_clear(hsh).should.equal?(hsh) hsh.should == {} end end @@ -124,7 +138,7 @@ describe "C-API Hash function" do end it "returns an Enumerator when no block is passed" do - @s.rb_hash_delete_if({a: 1}).should be_an_instance_of(Enumerator) + @s.rb_hash_delete_if({a: 1}).should.instance_of?(Enumerator) end end @@ -139,11 +153,11 @@ describe "C-API Hash function" do it "raises a KeyError if the key is not found and default is set" do @hsh.default = :d - -> { @s.rb_hash_fetch(@hsh, :c) }.should raise_error(KeyError) + -> { @s.rb_hash_fetch(@hsh, :c) }.should.raise(KeyError) end it "raises a KeyError if the key is not found and no default is set" do - -> { @s.rb_hash_fetch(@hsh, :c) }.should raise_error(KeyError) + -> { @s.rb_hash_fetch(@hsh, :c) }.should.raise(KeyError) end context "when key is not found" do @@ -178,6 +192,61 @@ describe "C-API Hash function" do end end + describe "rb_hash_bulk_insert" do + it 'inserts key-value pairs into the hash' do + arr = [:a, 1, :b, 2, :c, 3] + hash = {} + + @s.rb_hash_bulk_insert(arr.length, arr, hash) + + hash.should == {a: 1, b: 2, c: 3} + end + + it 'overwrites existing keys' do + arr = [:a, 4, :b, 5, :c, 6] + hash = {a: 1, b: 2} + + @s.rb_hash_bulk_insert(arr.length, arr, hash) + + hash.should == {a: 4, b: 5, c: 6} + end + + it 'uses the last key in the array if it appears multiple times' do + arr = [:a, 1, :b, 2, :a, 3] + hash = {} + + @s.rb_hash_bulk_insert(arr.length, arr, hash) + + hash.should == {a: 3, b: 2} + end + + it 'allows the array to be NULL if the length is zero' do + hash = {} + + @s.rb_hash_bulk_insert(0, nil, hash) + + hash.should == {} + end + + it 'does not include any keys after the given length' do + arr = [:a, 1, :b, 2, :c, 3, :d, 4] + hash = {} + + @s.rb_hash_bulk_insert(arr.length - 2, arr, hash) + + hash.should == {a: 1, b: 2, c: 3} + end + + it 'does not modify the hash if the length is zero' do + arr = [] + hash = {a: 1, b: 2} + + @s.rb_hash_bulk_insert(arr.length, arr, hash) + + hash.should == {a: 1, b: 2} + end + end + describe "rb_hash_size" do it "returns the size of the hash" do hsh = {fast: 'car', good: 'music'} @@ -197,14 +266,14 @@ describe "C-API Hash function" do it "does not return the default value if it exists" do hsh = Hash.new(0) - @s.rb_hash_lookup(hsh, :chunky).should be_nil - @s.rb_hash_lookup_nil(hsh, :chunky).should be_true + @s.rb_hash_lookup(hsh, :chunky).should == nil + @s.rb_hash_lookup_nil(hsh, :chunky).should == true end it "returns nil if the key does not exist" do hsh = { } - @s.rb_hash_lookup(hsh, :chunky).should be_nil - @s.rb_hash_lookup_nil(hsh, :chunky).should be_true + @s.rb_hash_lookup(hsh, :chunky).should == nil + @s.rb_hash_lookup_nil(hsh, :chunky).should == true end describe "rb_hash_lookup2" do @@ -222,7 +291,7 @@ describe "C-API Hash function" do it "returns undefined if that is the default value specified" do hsh = Hash.new(0) - @s.rb_hash_lookup2_default_undef(hsh, :chunky).should be_true + @s.rb_hash_lookup2_default_undef(hsh, :chunky).should == true end end end @@ -253,20 +322,20 @@ describe "C-API Hash function" do end it "raises a TypeError if the argument does not respond to #to_hash" do - -> { @s.rb_Hash(42) }.should raise_error(TypeError) + -> { @s.rb_Hash(42) }.should.raise(TypeError) end it "raises a TypeError if #to_hash does not return a hash" do h = BasicObject.new def h.to_hash; 42; end - -> { @s.rb_Hash(h) }.should raise_error(TypeError) + -> { @s.rb_Hash(h) }.should.raise(TypeError) end end describe "hash code functions" do it "computes a deterministic number" do hash_code = @s.compute_a_hash_code(53) - hash_code.should be_an_instance_of(Integer) + hash_code.should.instance_of?(Integer) hash_code.should == @s.compute_a_hash_code(53) @s.compute_a_hash_code(90).should == @s.compute_a_hash_code(90) end diff --git a/spec/ruby/optional/capi/integer_spec.rb b/spec/ruby/optional/capi/integer_spec.rb index e26735824e..f177374569 100644 --- a/spec/ruby/optional/capi/integer_spec.rb +++ b/spec/ruby/optional/capi/integer_spec.rb @@ -1,4 +1,4 @@ -# -*- encoding: binary -*- +# encoding: binary require_relative 'spec_helper' load_extension("integer") @@ -140,6 +140,23 @@ describe "CApiIntegerSpecs" do result.should == -1 @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE" end + + it "converts numbers near the fixnum limit successfully" do + result = @s.rb_integer_pack(0x7123_4567_89ab_cdef, @words, 1, 8, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x71" + + result = @s.rb_integer_pack(2**62-1, @words, 1, 8, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F" + + result = @s.rb_integer_pack(2**63-1, @words, 1, 8, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F" + end end end end diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb index c3c1189a43..35bd856e00 100644 --- a/spec/ruby/optional/capi/io_spec.rb +++ b/spec/ruby/optional/capi/io_spec.rb @@ -1,4 +1,5 @@ require_relative 'spec_helper' +require_relative '../../fixtures/io' load_extension('io') @@ -14,7 +15,7 @@ describe "C-API IO function" do end after :each do - @io.close unless @io.closed? + @io.close rm_r @name end @@ -33,7 +34,7 @@ describe "C-API IO function" do end it "returns the io" do - @o.rb_io_addstr(@io, "rb_io_addstr data").should eql(@io) + @o.rb_io_addstr(@io, "rb_io_addstr data").should.eql?(@io) end end @@ -117,15 +118,15 @@ describe "C-API IO function" do end after :each do - @io.close unless @io.closed? + @io.close rm_r @name end describe "rb_io_close" do it "closes an IO object" do - @io.closed?.should be_false + @io.closed?.should == false @o.rb_io_close(@io) - @io.closed?.should be_true + @io.closed?.should == true end end @@ -135,19 +136,19 @@ describe "C-API IO function" do end it "returns nil for non IO objects" do - @o.rb_io_check_io({}).should be_nil + @o.rb_io_check_io({}).should == nil end end describe "rb_io_check_closed" do it "does not raise an exception if the IO is not closed" do # The MRI function is void, so we use should_not raise_error - -> { @o.rb_io_check_closed(@io) }.should_not raise_error + -> { @o.rb_io_check_closed(@io) }.should_not.raise end it "raises an error if the IO is closed" do @io.close - -> { @o.rb_io_check_closed(@io) }.should raise_error(IOError) + -> { @o.rb_io_check_closed(@io) }.should.raise(IOError) end end @@ -156,7 +157,7 @@ describe "C-API IO function" do it "returns true when nonblock flag is set" do require 'io/nonblock' @o.rb_io_set_nonblock(@io) - @io.nonblock?.should be_true + @io.nonblock?.should == true end end end @@ -165,23 +166,28 @@ describe "C-API IO function" do # object is frozen, *not* if it's tainted. describe "rb_io_taint_check" do it "does not raise an exception if the IO is not frozen" do - -> { @o.rb_io_taint_check(@io) }.should_not raise_error + -> { @o.rb_io_taint_check(@io) }.should_not.raise end it "raises an exception if the IO is frozen" do @io.freeze - -> { @o.rb_io_taint_check(@io) }.should raise_error(RuntimeError) + -> { @o.rb_io_taint_check(@io) }.should.raise(RuntimeError) end end - describe "GetOpenFile" do + describe "rb_io_descriptor or GetOpenFile" do it "allows access to the system fileno" do @o.GetOpenFile_fd($stdin).should == 0 @o.GetOpenFile_fd($stdout).should == 1 @o.GetOpenFile_fd($stderr).should == 2 @o.GetOpenFile_fd(@io).should == @io.fileno end + + it "raises IOError if the IO is closed" do + @io.close + -> { @o.GetOpenFile_fd(@io) }.should.raise(IOError, "closed stream") + end end describe "rb_io_binmode" do @@ -191,7 +197,7 @@ describe "C-API IO function" do it "sets binmode" do @o.rb_io_binmode(@io) - @io.binmode?.should be_true + @io.binmode?.should == true end end end @@ -207,24 +213,24 @@ describe "C-API IO function" do end after :each do - @r_io.close unless @r_io.closed? - @w_io.close unless @w_io.closed? - @rw_io.close unless @rw_io.closed? + @r_io.close + @w_io.close + @rw_io.close rm_r @name end describe "rb_io_check_readable" do it "does not raise an exception if the IO is opened for reading" do # The MRI function is void, so we use should_not raise_error - -> { @o.rb_io_check_readable(@r_io) }.should_not raise_error + -> { @o.rb_io_check_readable(@r_io) }.should_not.raise end it "does not raise an exception if the IO is opened for read and write" do - -> { @o.rb_io_check_readable(@rw_io) }.should_not raise_error + -> { @o.rb_io_check_readable(@rw_io) }.should_not.raise end it "raises an IOError if the IO is not opened for reading" do - -> { @o.rb_io_check_readable(@w_io) }.should raise_error(IOError) + -> { @o.rb_io_check_readable(@w_io) }.should.raise(IOError) end end @@ -232,33 +238,83 @@ describe "C-API IO function" do describe "rb_io_check_writable" do it "does not raise an exception if the IO is opened for writing" do # The MRI function is void, so we use should_not raise_error - -> { @o.rb_io_check_writable(@w_io) }.should_not raise_error + -> { @o.rb_io_check_writable(@w_io) }.should_not.raise end it "does not raise an exception if the IO is opened for read and write" do - -> { @o.rb_io_check_writable(@rw_io) }.should_not raise_error + -> { @o.rb_io_check_writable(@rw_io) }.should_not.raise end it "raises an IOError if the IO is not opened for reading" do - -> { @o.rb_io_check_writable(@r_io) }.should raise_error(IOError) + -> { @o.rb_io_check_writable(@r_io) }.should.raise(IOError) end end describe "rb_io_wait_writable" do it "returns false if there is no error condition" do @o.errno = 0 - @o.rb_io_wait_writable(@w_io).should be_false + @o.rb_io_wait_writable(@w_io).should == false end it "raises an IOError if the IO is closed" do @w_io.close - -> { @o.rb_io_wait_writable(@w_io) }.should raise_error(IOError) + -> { @o.rb_io_wait_writable(@w_io) }.should.raise(IOError) + end + end + + describe "rb_io_maybe_wait_writable" do + it "returns mask for events if operation was interrupted" do + @o.rb_io_maybe_wait_writable(Errno::EINTR::Errno, @w_io, nil).should == IO::WRITABLE + end + + it "returns 0 if there is no error condition" do + @o.rb_io_maybe_wait_writable(0, @w_io, nil).should == 0 + end + + it "raises an IOError if the IO is closed" do + @w_io.close + -> { @o.rb_io_maybe_wait_writable(0, @w_io, nil) }.should.raise(IOError, "closed stream") + end + + it "raises an IOError if the IO is not initialized" do + -> { @o.rb_io_maybe_wait_writable(0, IO.allocate, nil) }.should.raise(IOError, "uninitialized stream") + end + + it "can be interrupted" do + IOSpec.exhaust_write_buffer(@w_io) + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @o.rb_io_maybe_wait_writable(0, @w_io, 10) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 end end describe "rb_thread_fd_writable" do it "waits til an fd is ready for writing" do - @o.rb_thread_fd_writable(@w_io).should be_nil + @o.rb_thread_fd_writable(@w_io).should == nil + end + end + + describe "rb_thread_fd_select" do + it "waits until an fd is ready for reading" do + @w_io.write "rb_thread_fd_select" + @o.rb_thread_fd_select_read(@r_io).should == 1 + end + + it "waits until an fd is ready for writing" do + @o.rb_thread_fd_select_write(@w_io).should == 1 + end + + it "waits until an fd is ready for writing with timeout" do + @o.rb_thread_fd_select_timeout(@w_io).should == 1 end end @@ -266,14 +322,14 @@ describe "C-API IO function" do describe "rb_io_wait_readable" do it "returns false if there is no error condition" do @o.errno = 0 - @o.rb_io_wait_readable(@r_io, false).should be_false + @o.rb_io_wait_readable(@r_io, false).should == false end it "raises and IOError if passed a closed stream" do @r_io.close -> { @o.rb_io_wait_readable(@r_io, false) - }.should raise_error(IOError) + }.should.raise(IOError) end it "blocks until the io is readable and returns true" do @@ -284,12 +340,59 @@ describe "C-API IO function" do end @o.errno = Errno::EAGAIN.new.errno - @o.rb_io_wait_readable(@r_io, true).should be_true + @o.rb_io_wait_readable(@r_io, true).should == true @o.instance_variable_get(:@read_data).should == "rb_io_wait_re" thr.join end end + + describe "rb_io_maybe_wait_readable" do + it "returns mask for events if operation was interrupted" do + @o.rb_io_maybe_wait_readable(Errno::EINTR::Errno, @r_io, nil, false).should == IO::READABLE + end + + it "returns 0 if there is no error condition" do + @o.rb_io_maybe_wait_readable(0, @r_io, nil, false).should == 0 + end + + it "blocks until the io is readable and returns events that actually occurred" do + @o.instance_variable_set :@write_data, false + thr = Thread.new do + Thread.pass until @o.instance_variable_get(:@write_data) + @w_io.write "rb_io_wait_readable" + end + + @o.rb_io_maybe_wait_readable(Errno::EAGAIN::Errno, @r_io, IO::READABLE, true).should == IO::READABLE + @o.instance_variable_get(:@read_data).should == "rb_io_wait_re" + + thr.join + end + + it "can be interrupted" do + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @o.rb_io_maybe_wait_readable(0, @r_io, 10, false) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end + + it "raises an IOError if the IO is closed" do + @r_io.close + -> { @o.rb_io_maybe_wait_readable(0, @r_io, nil, false) }.should.raise(IOError, "closed stream") + end + + it "raises an IOError if the IO is not initialized" do + -> { @o.rb_io_maybe_wait_readable(0, IO.allocate, nil, false) }.should.raise(IOError, "uninitialized stream") + end + end end describe "rb_thread_wait_fd" do @@ -303,7 +406,7 @@ describe "C-API IO function" do Thread.pass until start - @o.rb_thread_wait_fd(@r_io).should be_nil + @o.rb_thread_wait_fd(@r_io).should == nil thr.join end @@ -329,10 +432,240 @@ describe "C-API IO function" do @o.rb_wait_for_single_fd(@r_io, 1, 0, 0).should == 0 end end + + describe "rb_io_maybe_wait" do + it "waits til an fd is ready for reading" do + start = false + thr = Thread.new do + start = true + sleep 0.05 + @w_io.write "rb_io_maybe_wait" + end + + Thread.pass until start + + @o.rb_io_maybe_wait(Errno::EAGAIN::Errno, @r_io, IO::READABLE, nil).should == IO::READABLE + + thr.join + end + + it "returns mask for events if operation was interrupted" do + @o.rb_io_maybe_wait(Errno::EINTR::Errno, @w_io, IO::WRITABLE, nil).should == IO::WRITABLE + end + + it "raises an IOError if the IO is closed" do + @w_io.close + -> { @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil) }.should.raise(IOError, "closed stream") + end + + it "raises an IOError if the IO is not initialized" do + -> { @o.rb_io_maybe_wait(0, IO.allocate, IO::WRITABLE, nil) }.should.raise(IOError, "uninitialized stream") + end + + it "can be interrupted when waiting for READABLE event" do + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @o.rb_io_maybe_wait(0, @r_io, IO::READABLE, 10) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end + + it "can be interrupted when waiting for WRITABLE event" do + IOSpec.exhaust_write_buffer(@w_io) + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, 10) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end + end + + describe "rb_io_mode" do + it "returns the mode" do + (@o.rb_io_mode(@r_io) & 0b11).should == 0b01 + (@o.rb_io_mode(@w_io) & 0b11).should == 0b10 + (@o.rb_io_mode(@rw_io) & 0b11).should == 0b11 + end + end + + describe "rb_io_path" do + it "returns the IO#path" do + @o.rb_io_path(@r_io).should == @r_io.path + @o.rb_io_path(@rw_io).should == @rw_io.path + @o.rb_io_path(@rw_io).should == @name + end + end + + describe "rb_io_closed_p" do + it "returns false when io is not closed" do + @o.rb_io_closed_p(@r_io).should == false + @r_io.closed?.should == false + end + + it "returns true when io is closed" do + @r_io.close + + @o.rb_io_closed_p(@r_io).should == true + @r_io.closed?.should == true + end + end + + quarantine! do # "Errno::EBADF: Bad file descriptor" at closing @r_io, @rw_io etc in the after :each hook + describe "rb_io_open_descriptor" do + it "creates a new IO instance" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.should.is_a?(IO) + end + + it "return an instance of the specified class" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.class.should == File + + io = @o.rb_io_open_descriptor(IO, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.class.should == IO + end + + it "sets the specified file descriptor" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.fileno.should == @r_io.fileno + end + + it "sets the specified path" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.path.should == "a.txt" + end + + it "sets the specified mode" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, CApiIOSpecs::FMODE_BINMODE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.should.binmode? + + io = @o.rb_io_open_descriptor(File, @r_io.fileno, CApiIOSpecs::FMODE_TEXTMODE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.should_not.binmode? + end + + it "sets the specified timeout" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.timeout.should == 60 + end + + it "sets the specified internal encoding" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.internal_encoding.should == Encoding::US_ASCII + end + + it "sets the specified external encoding" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.external_encoding.should == Encoding::UTF_8 + end + + it "does not apply the specified encoding flags" do + name = tmp("rb_io_open_descriptor_specs") + File.write(name, "123\r\n456\n89") + file = File.open(name, "r") + + io = @o.rb_io_open_descriptor(File, file.fileno, CApiIOSpecs::FMODE_READABLE, "a.txt", 60, "US-ASCII", "UTF-8", CApiIOSpecs::ECONV_UNIVERSAL_NEWLINE_DECORATOR, {}) + io.read_nonblock(20).should == "123\r\n456\n89" + ensure + file.close + rm_r name + end + + it "ignores the IO open options" do + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {external_encoding: "windows-1251"}) + io.external_encoding.should == Encoding::UTF_8 + + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {internal_encoding: "windows-1251"}) + io.internal_encoding.should == Encoding::US_ASCII + + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {encoding: "windows-1251:binary"}) + io.external_encoding.should == Encoding::UTF_8 + io.internal_encoding.should == Encoding::US_ASCII + + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {textmode: false}) + io.should_not.binmode? + + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {binmode: true}) + io.should_not.binmode? + + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {autoclose: false}) + io.should.autoclose? + + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {path: "a.txt"}) + io.path.should == "a.txt" + end + + it "ignores the IO encoding options" do + io = @o.rb_io_open_descriptor(File, @w_io.fileno, CApiIOSpecs::FMODE_WRITABLE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {crlf_newline: true}) + + io.write("123\r\n456\n89") + io.flush + + @r_io.read_nonblock(20).should == "123\r\n456\n89" + end + + it "allows wrong mode" do + io = @o.rb_io_open_descriptor(File, @w_io.fileno, CApiIOSpecs::FMODE_READABLE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {}) + io.should.is_a?(File) + + platform_is_not :windows do + -> { io.read_nonblock(1) }.should.raise(Errno::EBADF) + end + + platform_is :windows do + -> { io.read_nonblock(1) }.should.raise(IO::EWOULDBLOCKWaitReadable) + end + end + + it "tolerates NULL as rb_io_encoding *encoding parameter" do + io = @o.rb_io_open_descriptor_without_encoding(File, @r_io.fileno, 0, "a.txt", 60) + io.should.is_a?(File) + end + + it "deduplicates path String" do + path = "a.txt".dup + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {}) + io.path.should_not.equal?(path) + + path = "a.txt".freeze + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {}) + io.path.should_not.equal?(path) + end + + it "calls #to_str to convert a path to a String" do + path = Object.new + def path.to_str; "a.txt"; end + + io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {}) + + io.path.should == "a.txt" + end + end + end + + ruby_version_is "3.4" do + describe "rb_io_maybe_wait" do + it "returns nil if there is no error condition" do + @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil).should == nil + end + end + end end describe "rb_fd_fix_cloexec" do - before :each do @o = CApiIOSpecs.new @@ -345,13 +678,13 @@ describe "rb_fd_fix_cloexec" do end after :each do - @io.close unless @io.closed? + @io.close rm_r @name end it "sets close_on_exec on the IO" do @o.rb_fd_fix_cloexec(@io) - @io.close_on_exec?.should be_true + @io.close_on_exec?.should == true end end @@ -366,12 +699,90 @@ describe "rb_cloexec_open" do end after :each do - @io.close unless @io.nil? || @io.closed? + @io.close if @io rm_r @name end it "sets close_on_exec on the newly-opened IO" do @io = @o.rb_cloexec_open(@name, 0, 0) - @io.close_on_exec?.should be_true + @io.close_on_exec?.should == true + end +end + +describe "rb_cloexec_dup" do + before :each do + @o = CApiIOSpecs.new + @name = tmp("c_api_rb_io_specs") + touch @name + + @io = new_io @name, "r" + @dup = nil + end + + after :each do + @dup.close if @dup + @io.close + rm_r @name + end + + it "duplicates a file descriptor and sets close_on_exec" do + @dup = @o.rb_cloexec_dup(@io) + @dup.should.close_on_exec? + @dup.fileno.should_not == @io.fileno + end +end + +describe "rb_cloexec_fcntl_dupfd" do + before :each do + @o = CApiIOSpecs.new + @name = tmp("c_api_rb_io_specs") + touch @name + + @io = new_io @name, "r" + @dup = nil + end + + after :each do + @dup.close if @dup + @io.close + rm_r @name + end + + it "duplicates a file descriptor and sets close_on_exec" do + @dup = @o.rb_cloexec_fcntl_dupfd(@io, 3) + @dup.close_on_exec?.should == true + @dup.fileno.should_not == @io.fileno + end + + it "returns a file descriptor greater than or equal to minfd" do + @dup = @o.rb_cloexec_fcntl_dupfd(@io, 100) + @dup.fileno.should >= 100 + @dup.close_on_exec?.should == true + end +end + +describe "rb_io_t modes flags" do + before :each do + @o = CApiIOSpecs.new + @name = tmp("c_api_rb_io_specs") + touch @name + end + + after :each do + rm_r @name + end + + it "has the sync flag set if the IO object is synced in Ruby" do + File.open(@name) { |io| + io.sync = true + @o.rb_io_mode_sync_flag(io).should == true + } + end + + it "has the sync flag unset if the IO object is not synced in Ruby" do + File.open(@name) { |io| + io.sync = false + @o.rb_io_mode_sync_flag(io).should == false + } end end diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb index 7539e0c01c..62eb21448b 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 @@ -19,7 +28,7 @@ describe "C-API Kernel function" do describe "rb_need_block" do it "raises a LocalJumpError if no block is given" do - -> { @s.rb_need_block }.should raise_error(LocalJumpError) + -> { @s.rb_need_block }.should.raise(LocalJumpError) end it "does not raise a LocalJumpError if a block is given" do @@ -44,7 +53,7 @@ describe "C-API Kernel function" do it "calls the method with no function callback and no block" do ary = [1, 3, 5] - @s.rb_block_call_no_func(ary).should be_kind_of(Enumerator) + @s.rb_block_call_no_func(ary).should.is_a?(Enumerator) end it "calls the method with no function callback and a block" do @@ -56,7 +65,7 @@ describe "C-API Kernel function" do it "can pass extra data to the function" do ary = [3] - @s.rb_block_call_extra_data(ary).should equal(ary) + @s.rb_block_call_extra_data(ary).should.equal?(ary) end end @@ -69,14 +78,30 @@ describe "C-API Kernel function" do describe "rb_raise" do it "raises an exception" do - -> { @s.rb_raise({}) }.should raise_error(TypeError) + -> { @s.rb_raise({}) }.should.raise(TypeError) end it "terminates the function at the point it was called" do h = {} - -> { @s.rb_raise(h) }.should raise_error(TypeError) + -> { @s.rb_raise(h) }.should.raise(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(StandardError, "aaa") + end end describe "rb_throw" do @@ -100,7 +125,7 @@ describe "C-API Kernel function" do end it "raises an ArgumentError if there is no catch block for the symbol" do - -> { @s.rb_throw(nil) }.should raise_error(ArgumentError) + -> { @s.rb_throw(nil) }.should.raise(ArgumentError) end end @@ -126,31 +151,21 @@ describe "C-API Kernel function" do end it "raises an ArgumentError if there is no catch block for the symbol" do - -> { @s.rb_throw(nil) }.should raise_error(ArgumentError) + -> { @s.rb_throw(nil) }.should.raise(ArgumentError) end 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 @@ -158,13 +173,13 @@ describe "C-API Kernel function" do it "raises an exception from the value of errno" do -> do @s.rb_sys_fail("additional info") - end.should raise_error(SystemCallError, /additional info/) + end.should.raise(SystemCallError, /additional info/) end it "can take a NULL message" do -> do @s.rb_sys_fail(nil) - end.should raise_error(Errno::EPERM) + end.should.raise(Errno::EPERM) 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(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(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(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(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(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(SystemCallError, /[[:graph:]]+/) end end @@ -194,7 +231,7 @@ describe "C-API Kernel function" do end it "raises LocalJumpError when no block is given" do - -> { @s.rb_yield(1) }.should raise_error(LocalJumpError) + -> { @s.rb_yield(1) }.should.raise(LocalJumpError) end it "rb_yield to a block that breaks does not raise an error" do @@ -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 @@ -234,7 +269,7 @@ describe "C-API Kernel function" do end it "raises LocalJumpError when no block is given" do - -> { @s.rb_yield_splat([1, 2]) }.should raise_error(LocalJumpError) + -> { @s.rb_yield_splat([1, 2]) }.should.raise(LocalJumpError) end end @@ -266,7 +301,7 @@ describe "C-API Kernel function" do end it "raises LocalJumpError when no block is given" do - -> { @s.rb_yield_splat([1, 2]) }.should raise_error(LocalJumpError) + -> { @s.rb_yield_splat([1, 2]) }.should.raise(LocalJumpError) end end @@ -294,24 +329,39 @@ 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} - end.should raise_error(NameError) + @s.rb_protect_yield(77, proof) { |x| raise NameError } + end.should.raise(NameError) proof[0].should == 23 end 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} - end.should raise_error(NameError) + @s.rb_protect_yield(77, proof) { |x| raise NameError } + end.should.raise(NameError) proof[0].should == 23 proof[1].should == nil end 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 rb_errinfo() with the captured exception" do + proof = [] + @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 describe "rb_eval_string_protect" do @@ -325,7 +375,7 @@ describe "C-API Kernel function" do proof = [] -> do @s.rb_eval_string_protect('raise RuntimeError', proof) - end.should raise_error(RuntimeError) + end.should.raise(RuntimeError) proof.should == [23, nil] end end @@ -366,16 +416,28 @@ describe "C-API Kernel function" do end it "raises an exception if passed function raises an exception other than StandardError" do - -> { @s.rb_rescue(@exc_error_proc, nil, @rescue_proc_returns_arg, nil) }.should raise_error(Exception) + -> { @s.rb_rescue(@exc_error_proc, nil, @rescue_proc_returns_arg, nil) }.should.raise(Exception) end it "raises an exception if any exception is raised inside the 'rescue function'" do - -> { @s.rb_rescue(@std_error_proc, nil, @std_error_proc, nil) }.should raise_error(StandardError) + -> { @s.rb_rescue(@std_error_proc, nil, @std_error_proc, nil) }.should.raise(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 @@ -393,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' } @@ -401,15 +463,30 @@ describe "C-API Kernel function" do @s.rb_rescue2(run_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError).should == :exc -> { @s.rb_rescue2(type_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError) - }.should raise_error(Exception, 'custom error') + }.should.raise(Exception, 'custom error') end - ruby_bug "#17305", ""..."2.7" do - it "raises TypeError if one of the passed exceptions is not a Module" 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 "raises TypeError if one of the passed exceptions is not a Module" do + -> { + @s.rb_rescue2(-> *_ { raise RuntimeError, "foo" }, :no_exc, -> x { x }, :exc, Object.new, 42) + }.should.raise(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 @@ -428,12 +505,12 @@ describe "C-API Kernel function" do throw :thrown_value ScratchPad << :after_throw end - @s.rb_catch("thrown_value", proc).should be_nil + @s.rb_catch("thrown_value", proc).should == nil ScratchPad.recorded.should == [:before_throw] end it "raises an ArgumentError if the throw symbol isn't caught" do - -> { @s.rb_catch("foo", -> { throw :bar }) }.should raise_error(ArgumentError) + -> { @s.rb_catch("foo", -> { throw :bar }) }.should.raise(ArgumentError) end end @@ -454,12 +531,46 @@ describe "C-API Kernel function" do throw @tag ScratchPad << :after_throw end - @s.rb_catch_obj(@tag, proc).should be_nil + @s.rb_catch_obj(@tag, proc).should == nil ScratchPad.recorded.should == [:before_throw] end it "raises an ArgumentError if the throw symbol isn't caught" do - -> { @s.rb_catch("foo", -> { throw :bar }) }.should raise_error(ArgumentError) + -> { @s.rb_catch("foo", -> { throw :bar }) }.should.raise(ArgumentError) + 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 @@ -479,16 +590,37 @@ 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(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(exception_class) + + -> { + @s.rb_ensure(-> _ { @s.rb_raise({}) }, nil, -> _ { + $!.should.is_a?(TypeError) + @s.rb_errinfo.should.is_a?(TypeError) + }, nil) + }.should.raise(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 = -> *_ { } - -> { @s.rb_ensure(raise_proc, nil, proc, nil) }.should raise_error(RuntimeError, 'foo') + -> { @s.rb_ensure(raise_proc, nil, proc, nil) }.should.raise(RuntimeError, 'foo') end end @@ -496,12 +628,38 @@ describe "C-API Kernel function" do it "evaluates a string of ruby code" do @s.rb_eval_string("1+1").should == 2 end + + it "captures local variables when called within a method" do + a = 2 + @s.rb_eval_string("a+1").should == 3 + end + 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] + end + end end describe "rb_block_proc" do it "converts the implicit block into a proc" do proc = @s.rb_block_proc { 1+1 } - proc.should be_kind_of(Proc) + proc.should.is_a?(Proc) proc.call.should == 2 proc.should_not.lambda? end @@ -509,7 +667,7 @@ describe "C-API Kernel function" do it "passes through an existing lambda and does not convert to a proc" do b = -> { 1+1 } proc = @s.rb_block_proc(&b) - proc.should equal(b) + proc.should.equal?(b) proc.call.should == 2 proc.should.lambda? end @@ -518,7 +676,7 @@ describe "C-API Kernel function" do describe "rb_block_lambda" do it "converts the implicit block into a lambda" do proc = @s.rb_block_lambda { 1+1 } - proc.should be_kind_of(Proc) + proc.should.is_a?(Proc) proc.call.should == 2 proc.should.lambda? end @@ -526,7 +684,7 @@ describe "C-API Kernel function" do it "passes through an existing Proc and does not convert to a lambda" do b = proc { 1+1 } proc = @s.rb_block_lambda(&b) - proc.should equal(b) + proc.should.equal?(b) proc.call.should == 2 proc.should_not.lambda? end @@ -545,6 +703,12 @@ describe "C-API Kernel function" do end end + describe "ruby_vm_at_exit" do + it "runs a C function after the VM is terminated" do + ruby_exe("require #{kernel_path.inspect}; CApiKernelSpecs.new.ruby_vm_at_exit").should == "ruby_vm_at_exit hook ran\n" + end + end + describe "rb_f_sprintf" do it "returns a string according to format and arguments" do @s.rb_f_sprintf(["%d %f %s", 10, 2.5, "test"]).should == "10 2.500000 test" @@ -555,25 +719,86 @@ describe "C-API Kernel function" do it "returns a caller backtrace" do backtrace = @s.rb_make_backtrace lines = backtrace.select {|l| l =~ /#{__FILE__}/ } - lines.should_not be_empty + lines.should_not.empty? end end - describe "rb_obj_method" do - it "returns the method object for a symbol" do - method = @s.rb_obj_method("test", :size) - method.owner.should == String - method.name.to_sym.should == :size + describe "rb_funcallv" do + def empty + 42 + end + + def sum(a, b) + a + b + end + + it "calls a method" do + @s.rb_funcallv(self, :empty, []).should == 42 + @s.rb_funcallv(self, :sum, [1, 2]).should == 3 + 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 + + describe "rb_funcallv_kw" do + it "passes keyword arguments to the callee" 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 "returns the method object for a string" do - method = @s.rb_obj_method("test", "size") - method.owner.should == String - method.name.to_sym.should == :size + 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 + + 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(TypeError, 'no implicit conversion of Integer into Hash') end end - describe "rb_funcall3" do + 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_funcallv_kw(@s, :rb_keyword_given_p, [{a: 1}]).should == true + @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{}]).should == false + end + end + + describe "rb_funcallv_public" do before :each do @obj = Object.new class << @obj @@ -584,10 +809,11 @@ describe "C-API Kernel function" do end it "calls a public method" do - @s.rb_funcall3(@obj, :method_public).should == :method_public + @s.rb_funcallv_public(@obj, :method_public).should == :method_public end + it "does not call a private method" do - -> { @s.rb_funcall3(@obj, :method_private) }.should raise_error(NoMethodError, /private/) + -> { @s.rb_funcallv_public(@obj, :method_private) }.should.raise(NoMethodError, /private/) end end @@ -605,22 +831,99 @@ describe "C-API Kernel function" do @s.rb_funcall_many_args(@obj, :many_args).should == 15.downto(1).to_a end 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(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(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(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(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 diff --git a/spec/ruby/optional/capi/module_spec.rb b/spec/ruby/optional/capi/module_spec.rb index fde86d2223..b9c36f569f 100644 --- a/spec/ruby/optional/capi/module_spec.rb +++ b/spec/ruby/optional/capi/module_spec.rb @@ -22,6 +22,8 @@ describe "CApiModule" do it "sets a new constant on a module" do @m.rb_const_set(CApiModuleSpecs::C, :W, 7) CApiModuleSpecs::C::W.should == 7 + ensure + CApiModuleSpecs::C.send(:remove_const, :W) end it "sets an existing constant's value" do @@ -34,15 +36,15 @@ describe "CApiModule" do it "allows arbitrary names, including constant names not valid in Ruby" do -> { CApiModuleSpecs::C.const_set(:_INVALID, 1) - }.should raise_error(NameError, /wrong constant name/) + }.should.raise(NameError, /wrong constant name/) - @m.rb_const_set(CApiModuleSpecs::C, :_INVALID, 2) + suppress_warning { @m.rb_const_set(CApiModuleSpecs::C, :_INVALID, 2) } @m.rb_const_get(CApiModuleSpecs::C, :_INVALID).should == 2 # Ruby-level should still not allow access -> { CApiModuleSpecs::C.const_get(:_INVALID) - }.should raise_error(NameError, /wrong constant name/) + }.should.raise(NameError, /wrong constant name/) end end @@ -54,15 +56,15 @@ describe "CApiModule" do it "raises a TypeError if the constant is not a module" do ::CApiModuleSpecsGlobalConst = 7 - -> { @m.rb_define_module("CApiModuleSpecsGlobalConst") }.should raise_error(TypeError) + -> { @m.rb_define_module("CApiModuleSpecsGlobalConst") }.should.raise(TypeError) Object.send :remove_const, :CApiModuleSpecsGlobalConst end it "defines a new module at toplevel" do mod = @m.rb_define_module("CApiModuleSpecsModuleB") - mod.should be_kind_of(Module) + mod.should.is_a?(Module) mod.name.should == "CApiModuleSpecsModuleB" - ::CApiModuleSpecsModuleB.should be_kind_of(Module) + ::CApiModuleSpecsModuleB.should.is_a?(Module) Object.send :remove_const, :CApiModuleSpecsModuleB end end @@ -70,7 +72,7 @@ describe "CApiModule" do describe "rb_define_module_under" do it "creates a new module inside the inner class" do mod = @m.rb_define_module_under(CApiModuleSpecs, "ModuleSpecsModuleUnder1") - mod.should be_kind_of(Module) + mod.should.is_a?(Module) end it "sets the module name" do @@ -93,6 +95,8 @@ describe "CApiModule" do it "defines a new constant on a module" do @m.rb_define_const(CApiModuleSpecs::C, "V", 7) CApiModuleSpecs::C::V.should == 7 + ensure + CApiModuleSpecs::C.send(:remove_const, :V) end it "sets an existing constant's value" do @@ -106,26 +110,26 @@ describe "CApiModule" do describe "rb_const_defined" do # The fixture converts C boolean test to Ruby 'true' / 'false' it "returns C non-zero if a constant is defined" do - @m.rb_const_defined(CApiModuleSpecs::A, :X).should be_true + @m.rb_const_defined(CApiModuleSpecs::A, :X).should == true end it "returns C non-zero if a constant is defined in Object" do - @m.rb_const_defined(CApiModuleSpecs::A, :Module).should be_true + @m.rb_const_defined(CApiModuleSpecs::A, :Module).should == true end end describe "rb_const_defined_at" do # The fixture converts C boolean test to Ruby 'true' / 'false' it "returns C non-zero if a constant is defined" do - @m.rb_const_defined_at(CApiModuleSpecs::A, :X).should be_true + @m.rb_const_defined_at(CApiModuleSpecs::A, :X).should == true end it "does not search in ancestors for the constant" do - @m.rb_const_defined_at(CApiModuleSpecs::B, :X).should be_false + @m.rb_const_defined_at(CApiModuleSpecs::B, :X).should == false end it "does not search in Object" do - @m.rb_const_defined_at(CApiModuleSpecs::A, :Module).should be_false + @m.rb_const_defined_at(CApiModuleSpecs::A, :Module).should == false end end @@ -134,6 +138,10 @@ describe "CApiModule" do @m.rb_const_get(CApiModuleSpecs::A, :X).should == 1 end + it "returns a constant defined in the module for multiple constants" do + [:Q, :R, :S, :T].each { |x| @m.rb_const_get(CApiModuleSpecs::A, x).should == CApiModuleSpecs::A.const_get(x) } + end + it "returns a constant defined at toplevel" do @m.rb_const_get(CApiModuleSpecs::A, :Integer).should == Integer end @@ -158,11 +166,11 @@ describe "CApiModule" do it "allows arbitrary names, including constant names not valid in Ruby" do -> { CApiModuleSpecs::A.const_get(:_INVALID) - }.should raise_error(NameError, /wrong constant name/) + }.should.raise(NameError, /wrong constant name/) -> { @m.rb_const_get(CApiModuleSpecs::A, :_INVALID) - }.should raise_error(NameError, /uninitialized constant/) + }.should.raise(NameError, /uninitialized constant/) end end @@ -229,7 +237,7 @@ describe "CApiModule" do describe "rb_define_global_function" do it "defines a method on Kernel" do @m.rb_define_global_function("module_specs_global_function") - Kernel.should have_method(:module_specs_global_function) + Kernel.should.respond_to?(:module_specs_global_function) module_specs_global_function.should == :test_method end end @@ -238,14 +246,52 @@ describe "CApiModule" do it "defines a method on a class" do cls = Class.new @m.rb_define_method(cls, "test_method") - cls.should have_instance_method(:test_method) + cls.should.method_defined?(:test_method, false) cls.new.test_method.should == :test_method end + it "returns the correct arity when argc of the method in class is 0" do + cls = Class.new + @m.rb_define_method(cls, "test_method") + cls.new.method(:test_method).arity.should == 0 + end + + it "returns the correct arity when argc of the method in class is 1" do + @m.rb_define_method_1required(42).should == 42 + @m.method(:rb_define_method_1required).arity.should == 1 + end + + it "returns the correct arity when argc of the method in class is 2" do + @m.rb_define_method_2required(1, 2).should == 2 + @m.method(:rb_define_method_2required).arity.should == 2 + end + + it "defines a method taking variable arguments as a C array if the argument count is -1" do + @m.rb_define_method_varargs_1(1, 3, 7, 4).should == [1, 3, 7, 4] + end + + it "returns the correct arity when argc of the method in class is -1" do + @m.method(:rb_define_method_varargs_1).arity.should == -1 + end + + it "defines a method taking variable arguments as a Ruby array if the argument count is -2" do + @m.rb_define_method_varargs_2(1, 3, 7, 4).should == [1, 3, 7, 4] + end + + it "returns the correct arity when argc of the method in class is -2" do + @m.method(:rb_define_method_varargs_2).arity.should == -1 + end + it "defines a method on a module" do mod = Module.new @m.rb_define_method(mod, "test_method") - mod.should have_instance_method(:test_method) + mod.should.method_defined?(:test_method, false) + end + + it "returns the correct arity of the method in module" do + mod = Module.new + @m.rb_define_method(mod, "test_method") + mod.instance_method(:test_method).arity.should == 0 end end @@ -259,11 +305,22 @@ describe "CApiModule" do @mod.test_module_function.should == :test_method end + it "returns the correct arity of the module function" do + @mod.method(:test_module_function).arity.should == 0 + end + it "defines a private instance method" do cls = Class.new cls.include(@mod) - cls.should have_private_instance_method(:test_module_function) + cls.private_instance_methods(true).should.include?(:test_module_function) + end + + it "returns the correct arity for private instance method" do + cls = Class.new + cls.include(@mod) + + @mod.instance_method(:test_module_function).arity.should == 0 end end @@ -271,14 +328,14 @@ describe "CApiModule" do it "defines a private method on a class" do cls = Class.new @m.rb_define_private_method(cls, "test_method") - cls.should have_private_instance_method(:test_method) + cls.private_instance_methods(false).should.include?(:test_method) cls.new.send(:test_method).should == :test_method end it "defines a private method on a module" do mod = Module.new @m.rb_define_private_method(mod, "test_method") - mod.should have_private_instance_method(:test_method) + mod.private_instance_methods(false).should.include?(:test_method) end end @@ -286,14 +343,14 @@ describe "CApiModule" do it "defines a protected method on a class" do cls = Class.new @m.rb_define_protected_method(cls, "test_method") - cls.should have_protected_instance_method(:test_method) + cls.protected_instance_methods(false).should.include?(:test_method) cls.new.send(:test_method).should == :test_method end it "defines a protected method on a module" do mod = Module.new @m.rb_define_protected_method(mod, "test_method") - mod.should have_protected_instance_method(:test_method) + mod.protected_instance_methods(false).should.include?(:test_method) end end @@ -303,7 +360,7 @@ describe "CApiModule" do a = cls.new @m.rb_define_singleton_method a, "module_specs_singleton_method" a.module_specs_singleton_method.should == :test_method - -> { cls.new.module_specs_singleton_method }.should raise_error(NoMethodError) + -> { cls.new.module_specs_singleton_method }.should.raise(NoMethodError) end end @@ -319,16 +376,16 @@ describe "CApiModule" do it "undef'ines a method on a class" do @class.new.ruby_test_method.should == :ruby_test_method @m.rb_undef_method @class, "ruby_test_method" - @class.should_not have_instance_method(:ruby_test_method) + @class.should_not.method_defined?(:ruby_test_method) end it "undefines private methods also" do @m.rb_undef_method @class, "initialize_copy" - -> { @class.new.dup }.should raise_error(NoMethodError) + -> { @class.new.dup }.should.raise(NoMethodError) end it "does not raise exceptions when passed a missing name" do - -> { @m.rb_undef_method @class, "not_exist" }.should_not raise_error + -> { @m.rb_undef_method @class, "not_exist" }.should_not.raise end describe "when given a frozen Class" do @@ -337,11 +394,11 @@ describe "CApiModule" do end it "raises a FrozenError when passed a name" do - -> { @m.rb_undef_method @frozen, "ruby_test_method" }.should raise_error(FrozenError) + -> { @m.rb_undef_method @frozen, "ruby_test_method" }.should.raise(FrozenError) end it "raises a FrozenError when passed a missing name" do - -> { @m.rb_undef_method @frozen, "not_exist" }.should raise_error(FrozenError) + -> { @m.rb_undef_method @frozen, "not_exist" }.should.raise(FrozenError) end end end @@ -356,7 +413,7 @@ describe "CApiModule" do cls.new.ruby_test_method.should == :ruby_test_method @m.rb_undef cls, :ruby_test_method - cls.should_not have_instance_method(:ruby_test_method) + cls.should_not.method_defined?(:ruby_test_method) end end diff --git a/spec/ruby/optional/capi/mutex_spec.rb b/spec/ruby/optional/capi/mutex_spec.rb index 34659974f5..fad9b4d1a2 100644 --- a/spec/ruby/optional/capi/mutex_spec.rb +++ b/spec/ruby/optional/capi/mutex_spec.rb @@ -10,64 +10,64 @@ describe "C-API Mutex functions" do describe "rb_mutex_new" do it "creates a new mutex" do - @s.rb_mutex_new.should be_an_instance_of(Mutex) + @s.rb_mutex_new.should.instance_of?(Mutex) end end describe "rb_mutex_locked_p" do it "returns false if the mutex is not locked" do - @s.rb_mutex_locked_p(@m).should be_false + @s.rb_mutex_locked_p(@m).should == false end it "returns true if the mutex is locked" do @m.lock - @s.rb_mutex_locked_p(@m).should be_true + @s.rb_mutex_locked_p(@m).should == true end end describe "rb_mutex_trylock" do it "locks the mutex if not locked" do - @s.rb_mutex_trylock(@m).should be_true - @m.locked?.should be_true + @s.rb_mutex_trylock(@m).should == true + @m.locked?.should == true end it "returns false if the mutex is already locked" do @m.lock - @s.rb_mutex_trylock(@m).should be_false - @m.locked?.should be_true + @s.rb_mutex_trylock(@m).should == false + @m.locked?.should == true end end describe "rb_mutex_lock" do it "returns when the mutex isn't locked" do @s.rb_mutex_lock(@m).should == @m - @m.locked?.should be_true + @m.locked?.should == true end it "throws an exception when already locked in the same thread" do @m.lock - -> { @s.rb_mutex_lock(@m) }.should raise_error(ThreadError) - @m.locked?.should be_true + -> { @s.rb_mutex_lock(@m) }.should.raise(ThreadError) + @m.locked?.should == true end end describe "rb_mutex_unlock" do it "raises an exception when not locked" do - -> { @s.rb_mutex_unlock(@m) }.should raise_error(ThreadError) - @m.locked?.should be_false + -> { @s.rb_mutex_unlock(@m) }.should.raise(ThreadError) + @m.locked?.should == false end it "unlocks the mutex when locked" do @m.lock @s.rb_mutex_unlock(@m).should == @m - @m.locked?.should be_false + @m.locked?.should == false end end describe "rb_mutex_sleep" do it "throws an exception when the mutex is not locked" do - -> { @s.rb_mutex_sleep(@m, 0.1) }.should raise_error(ThreadError) - @m.locked?.should be_false + -> { @s.rb_mutex_sleep(@m, 0.1) }.should.raise(ThreadError) + @m.locked?.should == false end it "sleeps when the mutex is locked" do @@ -76,14 +76,27 @@ describe "C-API Mutex functions" do @s.rb_mutex_sleep(@m, 0.001) t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC) (t2 - t1).should >= 0 - @m.locked?.should be_true + @m.locked?.should == true end end describe "rb_mutex_synchronize" do it "calls the function while the mutex is locked" do - callback = -> { @m.locked?.should be_true } + callback = -> { @m.locked?.should == true } @s.rb_mutex_synchronize(@m, callback) end + + it "returns a value returned from a callback" do + callback = -> { :foo } + @s.rb_mutex_synchronize(@m, callback).should == :foo + end + + it "calls a C-function that accepts and returns non-VALUE values" do + @s.rb_mutex_synchronize_with_naughty_callback(@m).should == 42 + end + + it "calls a native function" do + @s.rb_mutex_synchronize_with_native_callback(@m, 42).should == 42 + end end end diff --git a/spec/ruby/optional/capi/numeric_spec.rb b/spec/ruby/optional/capi/numeric_spec.rb index 95213d3f2b..abfed13503 100644 --- a/spec/ruby/optional/capi/numeric_spec.rb +++ b/spec/ruby/optional/capi/numeric_spec.rb @@ -9,7 +9,7 @@ describe "CApiNumericSpecs" do describe "NUM2INT" do it "raises a TypeError if passed nil" do - -> { @s.NUM2INT(nil) }.should raise_error(TypeError) + -> { @s.NUM2INT(nil) }.should.raise(TypeError) end it "converts a Float" do @@ -33,7 +33,7 @@ describe "CApiNumericSpecs" do end it "raises a RangeError if the value is more than 32bits" do - -> { @s.NUM2INT(0xffff_ffff+1) }.should raise_error(RangeError) + -> { @s.NUM2INT(0xffff_ffff+1) }.should.raise(RangeError) end it "calls #to_int to coerce the value" do @@ -45,7 +45,7 @@ describe "CApiNumericSpecs" do describe "NUM2UINT" do it "raises a TypeError if passed nil" do - -> { @s.NUM2UINT(nil) }.should raise_error(TypeError) + -> { @s.NUM2UINT(nil) }.should.raise(TypeError) end it "converts a Float" do @@ -69,17 +69,17 @@ describe "CApiNumericSpecs" do end it "raises a RangeError if the value is more than 32bits" do - -> { @s.NUM2UINT(0xffff_ffff+1) }.should raise_error(RangeError) + -> { @s.NUM2UINT(0xffff_ffff+1) }.should.raise(RangeError) end it "raises a RangeError if the value is less than 32bits negative" do - -> { @s.NUM2UINT(-0x8000_0000-1) }.should raise_error(RangeError) + -> { @s.NUM2UINT(-0x8000_0000-1) }.should.raise(RangeError) end it "raises a RangeError if the value is more than 64bits" do -> do @s.NUM2UINT(0xffff_ffff_ffff_ffff+1) - end.should raise_error(RangeError) + end.should.raise(RangeError) end it "calls #to_int to coerce the value" do @@ -91,7 +91,7 @@ describe "CApiNumericSpecs" do describe "NUM2LONG" do it "raises a TypeError if passed nil" do - -> { @s.NUM2LONG(nil) }.should raise_error(TypeError) + -> { @s.NUM2LONG(nil) }.should.raise(TypeError) end it "converts a Float" do @@ -106,7 +106,7 @@ describe "CApiNumericSpecs" do @s.NUM2LONG(5).should == 5 end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do it "converts -1 to an signed number" do @s.NUM2LONG(-1).should == -1 end @@ -116,11 +116,11 @@ describe "CApiNumericSpecs" do end it "raises a RangeError if the value is more than 32bits" do - -> { @s.NUM2LONG(0xffff_ffff+1) }.should raise_error(RangeError) + -> { @s.NUM2LONG(0xffff_ffff+1) }.should.raise(RangeError) end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "converts -1 to an signed number" do @s.NUM2LONG(-1).should == -1 end @@ -132,7 +132,7 @@ describe "CApiNumericSpecs" do it "raises a RangeError if the value is more than 64bits" do -> do @s.NUM2LONG(0xffff_ffff_ffff_ffff+1) - end.should raise_error(RangeError) + end.should.raise(RangeError) end end @@ -145,7 +145,7 @@ describe "CApiNumericSpecs" do describe "NUM2SHORT" do it "raises a TypeError if passed nil" do - -> { @s.NUM2SHORT(nil) }.should raise_error(TypeError) + -> { @s.NUM2SHORT(nil) }.should.raise(TypeError) end it "converts a Float" do @@ -161,7 +161,7 @@ describe "CApiNumericSpecs" do end it "raises a RangeError if the value is more than 32bits" do - -> { @s.NUM2SHORT(0xffff_ffff+1) }.should raise_error(RangeError) + -> { @s.NUM2SHORT(0xffff_ffff+1) }.should.raise(RangeError) end it "calls #to_int to coerce the value" do @@ -173,7 +173,7 @@ describe "CApiNumericSpecs" do describe "INT2NUM" do it "raises a TypeError if passed nil" do - -> { @s.INT2NUM(nil) }.should raise_error(TypeError) + -> { @s.INT2NUM(nil) }.should.raise(TypeError) end it "converts a Float" do @@ -181,7 +181,7 @@ describe "CApiNumericSpecs" do end it "raises a RangeError when passed a Bignum" do - -> { @s.INT2NUM(bignum_value) }.should raise_error(RangeError) + -> { @s.INT2NUM(bignum_value) }.should.raise(RangeError) end it "converts a Fixnum" do @@ -195,7 +195,7 @@ describe "CApiNumericSpecs" do describe "NUM2ULONG" do it "raises a TypeError if passed nil" do - -> { @s.NUM2ULONG(nil) }.should raise_error(TypeError) + -> { @s.NUM2ULONG(nil) }.should.raise(TypeError) end it "converts a Float" do @@ -210,7 +210,7 @@ describe "CApiNumericSpecs" do @s.NUM2ULONG(5).should == 5 end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do it "converts -1 to an unsigned number" do @s.NUM2ULONG(-1).should == 4294967295 end @@ -227,11 +227,11 @@ describe "CApiNumericSpecs" do end it "raises a RangeError if the value is more than 32bits" do - -> { @s.NUM2ULONG(0xffff_ffff+1) }.should raise_error(RangeError) + -> { @s.NUM2ULONG(0xffff_ffff+1) }.should.raise(RangeError) end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "converts -1 to an unsigned number" do @s.NUM2ULONG(-1).should == 18446744073709551615 end @@ -250,7 +250,7 @@ describe "CApiNumericSpecs" do it "raises a RangeError if the value is more than 64bits" do -> do @s.NUM2ULONG(0xffff_ffff_ffff_ffff+1) - end.should raise_error(RangeError) + end.should.raise(RangeError) end end @@ -308,11 +308,11 @@ describe "CApiNumericSpecs" do describe "NUM2DBL" do it "raises a TypeError if passed nil" do - -> { @s.NUM2DBL(nil) }.should raise_error(TypeError) + -> { @s.NUM2DBL(nil) }.should.raise(TypeError) end it "raises a TypeError if passed a String" do - -> { @s.NUM2DBL("1.2") }.should raise_error(TypeError) + -> { @s.NUM2DBL("1.2") }.should.raise(TypeError) end it "converts a Float" do @@ -348,13 +348,13 @@ describe "CApiNumericSpecs" do end it "raises a TypeError when passed an empty String" do - -> { @s.NUM2CHR("") }.should raise_error(TypeError) + -> { @s.NUM2CHR("") }.should.raise(TypeError) end end describe "rb_num_zerodiv" do it "raises a RuntimeError" do - -> { @s.rb_num_zerodiv() }.should raise_error(ZeroDivisionError, 'divided by 0') + -> { @s.rb_num_zerodiv() }.should.raise(ZeroDivisionError, 'divided by 0') end end @@ -386,7 +386,7 @@ describe "CApiNumericSpecs" do it "raises an ArgumentError when passed nil" do -> { @s.rb_cmpint(nil, 4) - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end end @@ -410,7 +410,7 @@ describe "CApiNumericSpecs" do obj = mock("rb_num_coerce_bin") obj.should_receive(:coerce).with(2).and_return(nil) - -> { @s.rb_num_coerce_bin(2, obj, :+) }.should raise_error(TypeError) + -> { @s.rb_num_coerce_bin(2, obj, :+) }.should.raise(TypeError) end end @@ -435,14 +435,14 @@ describe "CApiNumericSpecs" do obj.should_receive(:coerce).with(2).and_raise(RuntimeError.new("my error")) -> { @s.rb_num_coerce_cmp(2, obj, :<=>) - }.should raise_error(RuntimeError, "my error") + }.should.raise(RuntimeError, "my error") end it "returns nil if #coerce does not return an Array" do obj = mock("rb_num_coerce_cmp") obj.should_receive(:coerce).with(2).and_return(nil) - @s.rb_num_coerce_cmp(2, obj, :<=>).should be_nil + @s.rb_num_coerce_cmp(2, obj, :<=>).should == nil end end @@ -451,7 +451,7 @@ describe "CApiNumericSpecs" do obj = mock("rb_num_coerce_relop") obj.should_receive(:coerce).with(2).and_return([1, 2]) - @s.rb_num_coerce_relop(2, obj, :<).should be_true + @s.rb_num_coerce_relop(2, obj, :<).should == true end it "calls the specified method on the first argument returned by #coerce" do @@ -459,7 +459,7 @@ describe "CApiNumericSpecs" do obj.should_receive(:coerce).with(2).and_return([obj, 2]) obj.should_receive(:<).with(2).and_return(false) - @s.rb_num_coerce_relop(2, obj, :<).should be_false + @s.rb_num_coerce_relop(2, obj, :<).should == false end it "raises an ArgumentError if #<op> returns nil" do @@ -467,14 +467,14 @@ describe "CApiNumericSpecs" do obj.should_receive(:coerce).with(2).and_return([obj, 2]) obj.should_receive(:<).with(2).and_return(nil) - -> { @s.rb_num_coerce_relop(2, obj, :<) }.should raise_error(ArgumentError) + -> { @s.rb_num_coerce_relop(2, obj, :<) }.should.raise(ArgumentError) end it "raises an ArgumentError if #coerce does not return an Array" do obj = mock("rb_num_coerce_relop") obj.should_receive(:coerce).with(2).and_return(nil) - -> { @s.rb_num_coerce_relop(2, obj, :<) }.should raise_error(ArgumentError) + -> { @s.rb_num_coerce_relop(2, obj, :<) }.should.raise(ArgumentError) end end diff --git a/spec/ruby/optional/capi/object_spec.rb b/spec/ruby/optional/capi/object_spec.rb index be149898bd..8e907a5a8c 100644 --- a/spec/ruby/optional/capi/object_spec.rb +++ b/spec/ruby/optional/capi/object_spec.rb @@ -1,4 +1,5 @@ require_relative 'spec_helper' +require_relative 'fixtures/object' load_extension("object") @@ -30,6 +31,7 @@ describe "CApiObject" do class ObjectTest def initialize @foo = 7 + yield if block_given? end def foo @@ -62,7 +64,7 @@ describe "CApiObject" do it "allocates a new uninitialized object" do o = @o.rb_obj_alloc(CApiObjectSpecs::Alloc) o.class.should == CApiObjectSpecs::Alloc - o.initialized.should be_nil + o.initialized.should == nil end end @@ -75,19 +77,28 @@ describe "CApiObject" do obj2.foo.should == obj1.foo - obj2.should_not equal(obj1) + obj2.should_not.equal?(obj1) end end describe "rb_obj_call_init" do it "sends #initialize" do o = @o.rb_obj_alloc(CApiObjectSpecs::Alloc) - o.initialized.should be_nil + o.initialized.should == nil @o.rb_obj_call_init(o, 2, [:one, :two]) - o.initialized.should be_true + o.initialized.should == true o.arguments.should == [:one, :two] end + + it "passes the block to #initialize" do + v = nil + o = @o.rb_obj_alloc(ObjectTest) + @o.rb_obj_call_init(o, 0, []) do + v = :foo + end + v.should == :foo + end end describe "rb_is_instance_of" do @@ -115,6 +126,11 @@ describe "CApiObject" do @o.rb_respond_to(true, :object_id).should == true @o.rb_respond_to(14, :succ).should == true end + + it "returns 0 if the method has been defined as rb_f_notimplement" do + @o.respond_to?(:not_implemented_method).should == false + @o.rb_respond_to(@o, :not_implemented_method).should == false + end end describe "rb_obj_respond_to" do @@ -156,6 +172,20 @@ describe "CApiObject" do end end + describe "rb_obj_method" do + it "returns the method object for a symbol" do + method = @o.rb_obj_method("test", :size) + method.owner.should == String + method.name.to_sym.should == :size + end + + it "returns the method object for a string" do + method = @o.rb_obj_method("test", "size") + method.owner.should == String + method.name.to_sym.should == :size + end + end + describe "rb_method_boundp" do it "returns true when the given method is bound" do @o.rb_method_boundp(Object, :class, true).should == true @@ -189,7 +219,7 @@ describe "CApiObject" do end it "requires a ruby file" do - $:.unshift File.dirname(__FILE__) + $:.unshift __dir__ @o.rb_require() $foo.should == 7 end @@ -205,7 +235,7 @@ describe "CApiObject" do describe "rb_obj_instance_variables" do it "returns an array with instance variable names as symbols" do o = ObjectTest.new - @o.rb_obj_instance_variables(o).should include(:@foo) + @o.rb_obj_instance_variables(o).should.include?(:@foo) end end @@ -214,21 +244,21 @@ describe "CApiObject" do ary = [1, 2] ary.should_not_receive(:to_ary) - @o.rb_check_convert_type(ary, "Array", "to_ary").should equal(ary) + @o.rb_check_convert_type(ary, "Array", "to_ary").should.equal?(ary) end it "returns the passed object and does not call the converting method if the object is a subclass of the specified type" do obj = CApiObjectSpecs::SubArray.new obj.should_not_receive(:to_array) - @o.rb_check_convert_type(obj, "Array", "to_array").should equal(obj) + @o.rb_check_convert_type(obj, "Array", "to_array").should.equal?(obj) end it "returns nil if the converting method returns nil" do obj = mock("rb_check_convert_type") obj.should_receive(:to_array).and_return(nil) - @o.rb_check_convert_type(obj, "Array", "to_array").should be_nil + @o.rb_check_convert_type(obj, "Array", "to_array").should == nil end it "raises a TypeError if the converting method returns an object that is not the specified type" do @@ -237,7 +267,7 @@ describe "CApiObject" do -> do @o.rb_check_convert_type(obj, "Array", "to_array") - end.should raise_error(TypeError) + end.should.raise(TypeError) end end @@ -246,14 +276,14 @@ describe "CApiObject" do ary = [1, 2] ary.should_not_receive(:to_ary) - @o.rb_convert_type(ary, "Array", "to_ary").should equal(ary) + @o.rb_convert_type(ary, "Array", "to_ary").should.equal?(ary) end it "returns the passed object and does not call the converting method if the object is a subclass of the specified type" do obj = CApiObjectSpecs::SubArray.new obj.should_not_receive(:to_array) - @o.rb_convert_type(obj, "Array", "to_array").should equal(obj) + @o.rb_convert_type(obj, "Array", "to_array").should.equal?(obj) end it "raises a TypeError if the converting method returns nil" do @@ -262,7 +292,7 @@ describe "CApiObject" do -> do @o.rb_convert_type(obj, "Array", "to_array") - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if the converting method returns an object that is not the specified type" do @@ -271,109 +301,109 @@ describe "CApiObject" do -> do @o.rb_convert_type(obj, "Array", "to_array") - end.should raise_error(TypeError) + end.should.raise(TypeError) end end describe "rb_check_array_type" do it "returns the argument if it's an Array" do x = Array.new - @o.rb_check_array_type(x).should equal(x) + @o.rb_check_array_type(x).should.equal?(x) end it "returns the argument if it's a kind of Array" do x = AryChild.new - @o.rb_check_array_type(x).should equal(x) + @o.rb_check_array_type(x).should.equal?(x) end it "returns nil when the argument does not respond to #to_ary" do - @o.rb_check_array_type(Object.new).should be_nil + @o.rb_check_array_type(Object.new).should == nil end it "sends #to_ary to the argument and returns the result if it's nil" do obj = mock("to_ary") obj.should_receive(:to_ary).and_return(nil) - @o.rb_check_array_type(obj).should be_nil + @o.rb_check_array_type(obj).should == nil end it "sends #to_ary to the argument and returns the result if it's an Array" do x = Array.new obj = mock("to_ary") obj.should_receive(:to_ary).and_return(x) - @o.rb_check_array_type(obj).should equal(x) + @o.rb_check_array_type(obj).should.equal?(x) end it "sends #to_ary to the argument and returns the result if it's a kind of Array" do x = AryChild.new obj = mock("to_ary") obj.should_receive(:to_ary).and_return(x) - @o.rb_check_array_type(obj).should equal(x) + @o.rb_check_array_type(obj).should.equal?(x) end it "sends #to_ary to the argument and raises TypeError if it's not a kind of Array" do obj = mock("to_ary") obj.should_receive(:to_ary).and_return(Object.new) - -> { @o.rb_check_array_type obj }.should raise_error(TypeError) + -> { @o.rb_check_array_type obj }.should.raise(TypeError) end it "does not rescue exceptions raised by #to_ary" do obj = mock("to_ary") obj.should_receive(:to_ary).and_raise(FrozenError) - -> { @o.rb_check_array_type obj }.should raise_error(FrozenError) + -> { @o.rb_check_array_type obj }.should.raise(FrozenError) end end describe "rb_check_string_type" do it "returns the argument if it's a String" do x = String.new - @o.rb_check_string_type(x).should equal(x) + @o.rb_check_string_type(x).should.equal?(x) end it "returns the argument if it's a kind of String" do x = StrChild.new - @o.rb_check_string_type(x).should equal(x) + @o.rb_check_string_type(x).should.equal?(x) end it "returns nil when the argument does not respond to #to_str" do - @o.rb_check_string_type(Object.new).should be_nil + @o.rb_check_string_type(Object.new).should == nil end it "sends #to_str to the argument and returns the result if it's nil" do obj = mock("to_str") obj.should_receive(:to_str).and_return(nil) - @o.rb_check_string_type(obj).should be_nil + @o.rb_check_string_type(obj).should == nil end it "sends #to_str to the argument and returns the result if it's a String" do x = String.new obj = mock("to_str") obj.should_receive(:to_str).and_return(x) - @o.rb_check_string_type(obj).should equal(x) + @o.rb_check_string_type(obj).should.equal?(x) end it "sends #to_str to the argument and returns the result if it's a kind of String" do x = StrChild.new obj = mock("to_str") obj.should_receive(:to_str).and_return(x) - @o.rb_check_string_type(obj).should equal(x) + @o.rb_check_string_type(obj).should.equal?(x) end it "sends #to_str to the argument and raises TypeError if it's not a kind of String" do obj = mock("to_str") obj.should_receive(:to_str).and_return(Object.new) - -> { @o.rb_check_string_type obj }.should raise_error(TypeError) + -> { @o.rb_check_string_type obj }.should.raise(TypeError) end it "does not rescue exceptions raised by #to_str" do obj = mock("to_str") obj.should_receive(:to_str).and_raise(RuntimeError) - -> { @o.rb_check_string_type obj }.should raise_error(RuntimeError) + -> { @o.rb_check_string_type obj }.should.raise(RuntimeError) end end describe "rb_check_to_integer" do it "returns the object when passed a Fixnum" do - @o.rb_check_to_integer(5, "to_int").should equal(5) + @o.rb_check_to_integer(5, "to_int").should.equal?(5) end it "returns the object when passed a Bignum" do @@ -384,7 +414,7 @@ describe "CApiObject" do obj = mock("rb_check_to_integer") obj.should_receive(:to_integer).and_return(10) - @o.rb_check_to_integer(obj, "to_integer").should equal(10) + @o.rb_check_to_integer(obj, "to_integer").should.equal?(10) end it "calls the converting method and returns a Bignum value" do @@ -398,36 +428,27 @@ describe "CApiObject" do obj = mock("rb_check_to_integer") obj.should_receive(:to_integer).and_return(nil) - @o.rb_check_to_integer(obj, "to_integer").should be_nil + @o.rb_check_to_integer(obj, "to_integer").should == nil end it "returns nil when the converting method does not return an Integer" do obj = mock("rb_check_to_integer") obj.should_receive(:to_integer).and_return("string") - @o.rb_check_to_integer(obj, "to_integer").should be_nil + @o.rb_check_to_integer(obj, "to_integer").should == nil end end describe "FL_ABLE" do it "returns correct boolean for type" do - @o.FL_ABLE(Object.new).should be_true - @o.FL_ABLE(true).should be_false - @o.FL_ABLE(nil).should be_false - @o.FL_ABLE(1).should be_false + @o.FL_ABLE(Object.new).should == true + @o.FL_ABLE(true).should == false + @o.FL_ABLE(nil).should == false + @o.FL_ABLE(1).should == false end end describe "FL_TEST" do - ruby_version_is ''...'2.7' do - it "returns correct status for FL_TAINT" do - obj = Object.new - @o.FL_TEST(obj, "FL_TAINT").should == 0 - obj.taint - @o.FL_TEST(obj, "FL_TAINT").should_not == 0 - end - end - it "returns correct status for FL_FREEZE" do obj = Object.new @o.FL_TEST(obj, "FL_FREEZE").should == 0 @@ -455,9 +476,24 @@ describe "CApiObject" do it "returns the singleton class if it exists" do o = ObjectTest.new - @o.rb_class_of(o).should equal ObjectTest + @o.rb_class_of(o).should.equal? ObjectTest s = o.singleton_class - @o.rb_class_of(o).should equal s + @o.rb_class_of(o).should.equal? s + end + end + + describe "rb_obj_class" do + it "returns the class of an object" do + @o.rb_obj_class(nil).should == NilClass + @o.rb_obj_class(0).should == Integer + @o.rb_obj_class(0.1).should == Float + @o.rb_obj_class(ObjectTest.new).should == ObjectTest + end + + it "does not return the singleton class if it exists" do + o = ObjectTest.new + o.singleton_class + @o.rb_obj_class(o).should.equal? ObjectTest end end @@ -467,6 +503,10 @@ describe "CApiObject" do @o.rb_obj_classname(0).should == 'Integer' @o.rb_obj_classname(0.1).should == 'Float' @o.rb_obj_classname(ObjectTest.new).should == 'ObjectTest' + + o = ObjectTest.new + o.singleton_class + @o.rb_obj_classname(o).should == 'ObjectTest' end end @@ -480,20 +520,58 @@ describe "CApiObject" do @o.rb_is_type_array([]).should == true @o.rb_is_type_array(DescArray.new).should == true @o.rb_is_type_module(ObjectTest).should == false + @o.rb_is_type_module(Module.new).should == true @o.rb_is_type_class(ObjectTest).should == true @o.rb_is_type_data(Time.now).should == true end + + it "returns T_FILE for instances of IO and subclasses" do + STDERR.class.should == IO + @o.rb_is_rb_type_p_file(STDERR).should == true + + File.open(__FILE__) do |f| + f.class.should == File + @o.rb_is_rb_type_p_file(f).should == true + end + + require 'socket' + TCPServer.open(0) do |s| + @o.rb_is_rb_type_p_file(s).should == true + end + end + end + + describe "rb_check_type" do + it "checks if the object is of the given type" do + @o.rb_check_type(nil, nil).should == true + @o.rb_check_type(ObjectTest.new, Object.new).should == true + @o.rb_check_type([], []).should == true + @o.rb_check_type(Class.new(Array).new, []).should == true + @o.rb_check_type(ObjectTest, Object).should == true + end + + it "raises an exception if the object is not of the expected type" do + -> { + @o.rb_check_type([], Object.new) + }.should.raise(TypeError, 'wrong argument type Array (expected Object)') + + -> { + @o.rb_check_type(ObjectTest, Module.new) + }.should.raise(TypeError, 'wrong argument type Class (expected Module)') + + -> { + @o.rb_check_type(nil, "string") + }.should.raise(TypeError, 'wrong argument type nil (expected String)') + end end describe "rb_type_p" do it "returns whether object is of the given type" do - class DescArray < Array - end @o.rb_is_rb_type_p_nil(nil).should == true @o.rb_is_rb_type_p_object([]).should == false @o.rb_is_rb_type_p_object(ObjectTest.new).should == true @o.rb_is_rb_type_p_array([]).should == true - @o.rb_is_rb_type_p_array(DescArray.new).should == true + @o.rb_is_rb_type_p_array(Class.new(Array).new).should == true @o.rb_is_rb_type_p_module(ObjectTest).should == false @o.rb_is_rb_type_p_class(ObjectTest).should == true @o.rb_is_rb_type_p_data(Time.now).should == true @@ -502,12 +580,10 @@ describe "CApiObject" do describe "BUILTIN_TYPE" do it "returns the type constant for the object" do - class DescArray < Array - end @o.rb_is_builtin_type_object([]).should == false @o.rb_is_builtin_type_object(ObjectTest.new).should == true @o.rb_is_builtin_type_array([]).should == true - @o.rb_is_builtin_type_array(DescArray.new).should == true + @o.rb_is_builtin_type_array(Class.new(Array).new).should == true @o.rb_is_builtin_type_module(ObjectTest).should == false @o.rb_is_builtin_type_class(ObjectTest).should == true @o.rb_is_builtin_type_data(Time.now).should == true @@ -516,49 +592,49 @@ describe "CApiObject" do describe "RTEST" do it "returns C false if passed Qfalse" do - @o.RTEST(false).should be_false + @o.RTEST(false).should == false end it "returns C false if passed Qnil" do - @o.RTEST(nil).should be_false + @o.RTEST(nil).should == false end it "returns C true if passed Qtrue" do - @o.RTEST(true).should be_true + @o.RTEST(true).should == true end it "returns C true if passed a Symbol" do - @o.RTEST(:test).should be_true + @o.RTEST(:test).should == true end it "returns C true if passed an Object" do - @o.RTEST(Object.new).should be_true + @o.RTEST(Object.new).should == true end end describe "rb_special_const_p" do it "returns true if passed Qfalse" do - @o.rb_special_const_p(false).should be_true + @o.rb_special_const_p(false).should == true end it "returns true if passed Qtrue" do - @o.rb_special_const_p(true).should be_true + @o.rb_special_const_p(true).should == true end it "returns true if passed Qnil" do - @o.rb_special_const_p(nil).should be_true + @o.rb_special_const_p(nil).should == true end it "returns true if passed a Symbol" do - @o.rb_special_const_p(:test).should be_true + @o.rb_special_const_p(:test).should == true end it "returns true if passed a Fixnum" do - @o.rb_special_const_p(10).should be_true + @o.rb_special_const_p(10).should == true end it "returns false if passed an Object" do - @o.rb_special_const_p(Object.new).should be_false + @o.rb_special_const_p(Object.new).should == false end end @@ -577,75 +653,19 @@ describe "CApiObject" do end describe "OBJ_TAINT" do - ruby_version_is ''...'2.7' do - it "taints the object" do - obj = mock("tainted") - @o.OBJ_TAINT(obj) - obj.tainted?.should be_true - end - end end describe "OBJ_TAINTED" do - ruby_version_is ''...'2.7' do - it "returns C true if the object is tainted" do - obj = mock("tainted") - obj.taint - @o.OBJ_TAINTED(obj).should be_true - end - - it "returns C false if the object is not tainted" do - obj = mock("untainted") - @o.OBJ_TAINTED(obj).should be_false - end - end end describe "OBJ_INFECT" do - ruby_version_is ''...'2.7' do - it "does not taint the first argument if the second argument is not tainted" do - host = mock("host") - source = mock("source") - @o.OBJ_INFECT(host, source) - host.tainted?.should be_false - end - - it "taints the first argument if the second argument is tainted" do - host = mock("host") - source = mock("source").taint - @o.OBJ_INFECT(host, source) - host.tainted?.should be_true - end - - it "does not untrust the first argument if the second argument is trusted" do - host = mock("host") - source = mock("source") - @o.OBJ_INFECT(host, source) - host.untrusted?.should be_false - end - - it "untrusts the first argument if the second argument is untrusted" do - host = mock("host") - source = mock("source").untrust - @o.OBJ_INFECT(host, source) - host.untrusted?.should be_true - end - - it "propagates both taint and distrust" do - host = mock("host") - source = mock("source").taint.untrust - @o.OBJ_INFECT(host, source) - host.tainted?.should be_true - host.untrusted?.should be_true - end - end end describe "rb_obj_freeze" do it "freezes the object passed to it" do obj = "" @o.rb_obj_freeze(obj).should == obj - obj.frozen?.should be_true + obj.frozen?.should == true end end @@ -654,7 +674,7 @@ describe "CApiObject" do obj = ObjectTest -> do @o.rb_obj_instance_eval(obj) { include Kernel } - end.should_not raise_error(NoMethodError) + end.should_not.raise(NoMethodError) end end @@ -666,34 +686,32 @@ describe "CApiObject" do end it "returns false if object passed to it is not frozen" do - obj = "" + obj = +"" @o.rb_obj_frozen_p(obj).should == false end end - describe "rb_obj_taint" do - ruby_version_is ''...'2.7' do - it "marks the object passed as tainted" do - obj = "" - obj.should_not.tainted? - @o.rb_obj_taint(obj) - obj.should.tainted? - end + describe "redefining frozen? works" do + it "allows an object to override frozen?" do + obj = CApiObjectRedefinitionSpecs.new - it "raises a FrozenError if the object passed is frozen" do - -> { @o.rb_obj_taint("".freeze) }.should raise_error(FrozenError) - end + obj.frozen?.should == false + obj.freeze + obj.frozen?.should == true end end + describe "rb_obj_taint" do + end + describe "rb_check_frozen" do it "raises a FrozenError if the obj is frozen" do - -> { @o.rb_check_frozen("".freeze) }.should raise_error(FrozenError) + -> { @o.rb_check_frozen("".freeze) }.should.raise(FrozenError) end it "does nothing when object isn't frozen" do - obj = "" - -> { @o.rb_check_frozen(obj) }.should_not raise_error(TypeError) + obj = +"" + -> { @o.rb_check_frozen(obj) }.should_not.raise(TypeError) end end @@ -701,13 +719,13 @@ describe "CApiObject" do it "converts an Integer to string" do obj = 1 i = @o.rb_any_to_s(obj) - i.should be_kind_of(String) + i.should.is_a?(String) end it "converts an Object to string" do obj = Object.new i = @o.rb_any_to_s(obj) - i.should be_kind_of(String) + i.should.is_a?(String) end end @@ -733,61 +751,61 @@ describe "CApiObject" do it "raises a TypeError if #to_int does not return an Integer" do x = mock("to_int") x.should_receive(:to_int).and_return("5") - -> { @o.rb_to_int(x) }.should raise_error(TypeError) + -> { @o.rb_to_int(x) }.should.raise(TypeError) end it "raises a TypeError if called with nil" do - -> { @o.rb_to_int(nil) }.should raise_error(TypeError) + -> { @o.rb_to_int(nil) }.should.raise(TypeError) end it "raises a TypeError if called with true" do - -> { @o.rb_to_int(true) }.should raise_error(TypeError) + -> { @o.rb_to_int(true) }.should.raise(TypeError) end it "raises a TypeError if called with false" do - -> { @o.rb_to_int(false) }.should raise_error(TypeError) + -> { @o.rb_to_int(false) }.should.raise(TypeError) end it "raises a TypeError if called with a String" do - -> { @o.rb_to_int("1") }.should raise_error(TypeError) + -> { @o.rb_to_int("1") }.should.raise(TypeError) end end describe "rb_equal" do it "returns true if the arguments are the same exact object" do s = "hello" - @o.rb_equal(s, s).should be_true + @o.rb_equal(s, s).should == true end it "calls == to check equality and coerces to true/false" do m = mock("string") m.should_receive(:==).and_return(8) - @o.rb_equal(m, "hello").should be_true + @o.rb_equal(m, "hello").should == true m2 = mock("string") m2.should_receive(:==).and_return(nil) - @o.rb_equal(m2, "hello").should be_false + @o.rb_equal(m2, "hello").should == false end end describe "rb_class_inherited_p" do it "returns true if mod equals arg" do - @o.rb_class_inherited_p(Array, Array).should be_true + @o.rb_class_inherited_p(Array, Array).should == true end it "returns true if mod is a subclass of arg" do - @o.rb_class_inherited_p(Array, Object).should be_true + @o.rb_class_inherited_p(Array, Object).should == true end it "returns nil if mod is not a subclass of arg" do - @o.rb_class_inherited_p(Array, Hash).should be_nil + @o.rb_class_inherited_p(Array, Hash).should == nil end it "raises a TypeError if arg is no class or module" do ->{ @o.rb_class_inherited_p(1, 2) - }.should raise_error(TypeError) + }.should.raise(TypeError) end end @@ -880,15 +898,15 @@ describe "CApiObject" do end end - # The `generic_iv_tbl` table and `*_generic_ivar` functions are for mutable + # The `generic_fields_tbl` table and `*_generic_ivar` functions are for mutable # objects which do not store ivars directly in MRI such as RString, because # there is no member iv_index_tbl (ivar table) such as in RObject and RClass. describe "rb_copy_generic_ivar for objects which do not store ivars directly" do it "copies the instance variables from one object to another" do - original = "abc" + original = +"abc" original.instance_variable_set(:@foo, :bar) - clone = "def" + clone = +"def" @o.rb_copy_generic_ivar(clone, original) clone.instance_variable_get(:@foo).should == :bar end @@ -896,7 +914,7 @@ describe "CApiObject" do describe "rb_free_generic_ivar for objects which do not store ivars directly" do it "removes the instance variables from an object" do - o = "abc" + o = +"abc" o.instance_variable_set(:@baz, :flibble) @o.rb_free_generic_ivar(o) o.instance_variables.should == [] @@ -911,7 +929,7 @@ describe "CApiObject" do @o.rb_define_alloc_func(klass) obj = klass.allocate obj.class.should.equal?(klass) - obj.should have_instance_variable(:@from_custom_allocator) + obj.should.instance_variable_defined?(:@from_custom_allocator) end it "sets up the allocator for a subclass of String" do @@ -919,7 +937,7 @@ describe "CApiObject" do @o.rb_define_alloc_func(klass) obj = klass.allocate obj.class.should.equal?(klass) - obj.should have_instance_variable(:@from_custom_allocator) + obj.should.instance_variable_defined?(:@from_custom_allocator) obj.should == "" end @@ -928,7 +946,7 @@ describe "CApiObject" do @o.rb_define_alloc_func(klass) obj = klass.allocate obj.class.should.equal?(klass) - obj.should have_instance_variable(:@from_custom_allocator) + obj.should.instance_variable_defined?(:@from_custom_allocator) obj.should == [] end end @@ -976,5 +994,28 @@ describe "CApiObject" do @o.speced_allocator?(parent).should == true end end + + describe "rb_ivar_foreach" do + it "calls the callback function for each instance variable on an object" do + o = CApiObjectSpecs::IVars.new + ary = @o.rb_ivar_foreach(o) + ary.should == [:@a, 3, :@b, 7, :@c, 4] + end + + it "calls the callback function for each cvar and ivar on a class" do + exp = [:@@cvar, :foo, :@@cvar2, :bar, :@ivar, :baz] + + ary = @o.rb_ivar_foreach(CApiObjectSpecs::CVars) + ary.should == exp + end + + it "calls the callback function for each cvar and ivar on a module" do + exp = [:@@mvar, :foo, :@@mvar2, :bar, :@ivar, :baz] + + ary = @o.rb_ivar_foreach(CApiObjectSpecs::MVars) + ary.should == exp + end + + end end end diff --git a/spec/ruby/optional/capi/proc_spec.rb b/spec/ruby/optional/capi/proc_spec.rb index ff119416f6..1c250fee76 100644 --- a/spec/ruby/optional/capi/proc_spec.rb +++ b/spec/ruby/optional/capi/proc_spec.rb @@ -7,6 +7,8 @@ describe "C-API Proc function" do before :each do @p = CApiProcSpecs.new @prc = @p.rb_proc_new + @prc2 = @p.rb_proc_new_argv_n + @prc3 = @p.rb_proc_new_argc end describe "rb_proc_new" do @@ -15,6 +17,7 @@ describe "C-API Proc function" do end it "calls the C function wrapped by the Proc instance when sent #call" do + @p.rb_proc_new_arg.call().should == nil @prc.call(:foo_bar).should == ":foo_bar" @prc.call([:foo, :bar]).should == "[:foo, :bar]" end @@ -24,6 +27,30 @@ describe "C-API Proc function" do @prc[[:foo, :bar]].should == "[:foo, :bar]" end + it "calls the C function with the arg count in argc" do + @prc3.call().should == 0 + @prc3.call(:foo).should == 1 + @prc3.call(:foo, :bar).should == 2 + end + + it "calls the C function with arguments in argv" do + @prc2.call(1, :foo).should == :foo + @prc2.call(2, :foo, :bar).should == :bar + -> { @prc2.call(3, :foo, :bar) }.should.raise(ArgumentError) + end + + it "calls the C function with the block passed in blockarg" do + a_block = :foo.to_proc + @p.rb_proc_new_blockarg.call(&a_block).should == a_block + @p.rb_proc_new_blockarg.call().should == nil + end + + it "calls the C function and yields to the block passed in blockarg" do + @p.rb_proc_new_block_given_p.call() do + end.should == false + @p.rb_proc_new_block_given_p.call().should == false + end + it "returns a Proc instance correctly described in #inspect without source location" do @prc.inspect.should =~ /^#<Proc:([^ :@]*?)>$/ end @@ -55,22 +82,75 @@ describe "C-API Proc function" do end end + describe "rb_proc_call_kw" do + it "passes keyword arguments to the proc" do + prc = proc { |*args, **kw| [args, kw] } + + @p.rb_proc_call_kw(prc, [{}]).should == [[], {}] + @p.rb_proc_call_kw(prc, [{a: 1}]).should == [[], {a: 1}] + @p.rb_proc_call_kw(prc, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}] + @p.rb_proc_call_kw(prc, [{b: 2}, {}]).should == [[{b: 2}], {}] + end + + it "raises TypeError if the last argument is not a Hash" do + -> { + @p.rb_proc_call_kw(proc {}, [42]) + }.should.raise(TypeError, 'no implicit conversion of Integer into Hash') + end + end + + describe "rb_proc_call_with_block" do + it "calls the Proc and passes arguments and a block" do + prc = Proc.new { |a, b, &block| block.call(a * b) } + @p.rb_proc_call_with_block(prc, [6, 7], proc { |n| n * 2 }).should == 6 * 7 * 2 + end + + it "calls the Proc and passes arguments when a block is nil" do + prc = Proc.new { |a, b| a * b } + @p.rb_proc_call_with_block(prc, [6, 7], nil).should == 6 * 7 + end + end + + describe "rb_proc_call_with_block_kw" do + it "passes keyword arguments and a block to the proc" do + prc = proc { |*args, **kw, &block| [args, kw, block.call(42)] } + block = proc { |n| n } + + @p.rb_proc_call_with_block_kw(prc, [{}], block).should == [[], {}, 42] + @p.rb_proc_call_with_block_kw(prc, [{a: 1}], block).should == [[], {a: 1}, 42] + @p.rb_proc_call_with_block_kw(prc, [{b: 2}, {a: 1}], block).should == [[{b: 2}], {a: 1}, 42] + @p.rb_proc_call_with_block_kw(prc, [{b: 2}, {}], block).should == [[{b: 2}], {}, 42] + end + + it "raises TypeError if the last argument is not a Hash" do + -> { + @p.rb_proc_call_with_block_kw(proc {}, [42], proc {}) + }.should.raise(TypeError, 'no implicit conversion of Integer into Hash') + end + + it "passes keyword arguments to the proc when a block is nil" do + prc = proc { |*args, **kw| [args, kw] } + + @p.rb_proc_call_with_block_kw(prc, [{}], nil).should == [[], {}] + end + end + describe "rb_obj_is_proc" do it "returns true for Proc" do prc = Proc.new {|a,b| a * b } - @p.rb_obj_is_proc(prc).should be_true + @p.rb_obj_is_proc(prc).should == true end it "returns true for subclass of Proc" do prc = Class.new(Proc).new {} - @p.rb_obj_is_proc(prc).should be_true + @p.rb_obj_is_proc(prc).should == true end it "returns false for non Proc instances" do - @p.rb_obj_is_proc("aoeui").should be_false - @p.rb_obj_is_proc(123).should be_false - @p.rb_obj_is_proc(true).should be_false - @p.rb_obj_is_proc([]).should be_false + @p.rb_obj_is_proc("aoeui").should == false + @p.rb_obj_is_proc(123).should == false + @p.rb_obj_is_proc(true).should == false + @p.rb_obj_is_proc([]).should == false end end end @@ -88,48 +168,21 @@ describe "C-API when calling Proc.new from a C function" do # For example: C -> Ruby <- C -> Ruby means a C function called into Ruby # code which returned to C, then C called into Ruby code again. - ruby_version_is ""..."2.7" do - # Ruby -> C -> rb_funcall(Proc.new) - it "returns the Proc passed by the Ruby code calling the C function" do - prc = @p.rb_Proc_new(0) { :called } - prc.call.should == :called - end - - # Ruby -> C -> Ruby <- C -> rb_funcall(Proc.new) - it "returns the Proc passed to the Ruby method when the C function calls other Ruby methods before calling Proc.new" do - prc = @p.rb_Proc_new(1) { :called } - prc.call.should == :called - end - end - # Ruby -> C -> Ruby -> Proc.new it "raises an ArgumentError when the C function calls a Ruby method that calls Proc.new" do - def @p.Proc_new() Proc.new end - -> { @p.rb_Proc_new(2) { :called } }.should raise_error(ArgumentError) + -> { + @p.rb_Proc_new(2) { :called } + }.should.raise(ArgumentError) end # Ruby -> C -> Ruby -> C -> rb_funcall(Proc.new) it "raises an ArgumentError when the C function calls a Ruby method and that method calls a C function that calls Proc.new" do def @p.redispatch() rb_Proc_new(0) end - -> { @p.rb_Proc_new(3) { :called } }.should raise_error(ArgumentError) - end - - ruby_version_is ""..."2.7" do - # Ruby -> C -> Ruby -> C (with new block) -> rb_funcall(Proc.new) - it "returns the most recent Proc passed when the Ruby method called the C function" do - prc = @p.rb_Proc_new(4) { :called } - prc.call.should == :calling_with_block - end - - # Ruby -> C -> Ruby -> C (with new block) <- Ruby <- C -> # rb_funcall(Proc.new) - it "returns the Proc passed from the original Ruby call to the C function" do - prc = @p.rb_Proc_new(5) { :called } - prc.call.should == :called - end + -> { @p.rb_Proc_new(3) { :called } }.should.raise(ArgumentError) end # Ruby -> C -> Ruby -> block_given? it "returns false from block_given? in a Ruby method called by the C function" do - @p.rb_Proc_new(6).should be_false + @p.rb_Proc_new(6).should == false end end diff --git a/spec/ruby/optional/capi/range_spec.rb b/spec/ruby/optional/capi/range_spec.rb index 7a52dc7ff8..9213862aa4 100644 --- a/spec/ruby/optional/capi/range_spec.rb +++ b/spec/ruby/optional/capi/range_spec.rb @@ -31,8 +31,8 @@ describe "C-API Range function" do end it "raises an ArgumentError when the given start and end can't be compared by using #<=>" do - -> { @s.rb_range_new(1, mock('x')) }.should raise_error(ArgumentError) - -> { @s.rb_range_new(mock('x'), mock('y')) }.should raise_error(ArgumentError) + -> { @s.rb_range_new(1, mock('x')) }.should.raise(ArgumentError) + -> { @s.rb_range_new(mock('x'), mock('y')) }.should.raise(ArgumentError) end end @@ -41,7 +41,7 @@ describe "C-API Range function" do beg, fin, excl = @s.rb_range_values(10..20) beg.should == 10 fin.should == 20 - excl.should be_false + excl.should == false end it "stores the range properties of non-Range object" do @@ -62,34 +62,170 @@ describe "C-API Range function" do beg, fin, excl = @s.rb_range_values(range_like) beg.should == 10 fin.should == 20 - excl.should be_false + excl.should == false end end describe "rb_range_beg_len" do it "returns correct begin, length and result" do r = 2..5 - begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 10, 0) - result.should be_true + begp, lenp, result = @s.rb_range_beg_len(r, 10, 0) + result.should == true begp.should == 2 lenp.should == 4 end it "returns nil when not in range" do r = 2..5 - begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 1, 0) - result.should be_nil + begp, lenp, result = @s.rb_range_beg_len(r, 1, 0) + result.should == nil end it "raises a RangeError when not in range and err is 1" do r = -5..-1 - -> { @s.rb_range_beg_len(r, 0, 0, 1, 1) }.should raise_error(RangeError) + -> { @s.rb_range_beg_len(r, 1, 1) }.should.raise(RangeError) end it "returns nil when not in range and err is 0" do r = -5..-1 - begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 1, 0) - result.should be_nil + begp, lenp, result = @s.rb_range_beg_len(r, 1, 0) + result.should == nil + end + end + + describe "rb_arithmetic_sequence_extract" do + it "returns begin, end, step, exclude end of an instance of an Enumerator::ArithmeticSequence" do + enum = (10..20).step(5) + enum.should.kind_of?(Enumerator::ArithmeticSequence) + + @s.rb_arithmetic_sequence_extract(enum).should == [1, 10, 20, 5, false] + end + + it "returns begin, end, step, exclude end of an instance of a Range" do + range = (10..20) + @s.rb_arithmetic_sequence_extract(range).should == [1, 10, 20, 1, false] + end + + it "returns begin, end, step, exclude end of a non-Range object with Range properties" do + object = Object.new + def object.begin + 10 + end + def object.end + 20 + end + def object.exclude_end? + false + end + + @s.rb_arithmetic_sequence_extract(object).should == [1, 10, 20, 1, false] + end + + it "returns failed status if given object is not Enumerator::ArithmeticSequence or Range or Range-like object" do + object = Object.new + @s.rb_arithmetic_sequence_extract(object).should == [0] + end + end + + describe "rb_arithmetic_sequence_beg_len_step" do + it "returns correct begin, length, step and result" do + as = (2..5).step(5) + error_code = 0 + + success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code) + success.should == true + + beg.should == 2 + len.should == 4 + step.should == 5 + end + + it "takes into account excluded end boundary" do + as = (2...5).step(1) + error_code = 0 + + success, _, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code) + success.should == true + len.should == 3 + end + + it "adds length to negative begin boundary" do + as = (-2..5).step(1) + error_code = 0 + + success, beg, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code) + success.should == true + + beg.should == 4 + len.should == 2 + end + + it "adds length to negative end boundary" do + as = (2..-1).step(1) + error_code = 0 + + success, beg, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code) + success.should == true + + beg.should == 2 + len.should == 4 + end + + it "truncates arithmetic sequence length if end boundary greater than specified length value" do + as = (2..10).step(1) + error_code = 0 + + success, _, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code) + success.should == true + len.should == 4 + end + + it "returns inverted begin and end boundaries when step is negative" do + as = (2..5).step(-2) + error_code = 0 + + success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code) + success.should == true + + beg.should == 5 + len.should == 0 + step.should == -2 + end + + it "returns nil when not in range and error code = 0" do + as = (2..5).step(1) + error_code = 0 + + success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code) + success.should == nil + end + + it "returns nil when not in range, negative boundaries and error code = 0" do + as = (-5..-1).step(1) + error_code = 0 + + success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, 0) + success.should == nil + end + + it "returns begin, length and step and doesn't raise a RangeError when not in range and error code = 1" do + as = (2..5).step(1) + error_code = 1 + + success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code) + success.should == true + + beg.should == 2 + len.should == 4 + step.should == 1 + end + + it "returns nil and doesn't raise a RangeError when not in range, negative boundaries and error code = 1" do + as = (-5..-1).step(1) + error_code = 1 + + success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code) + success.should == nil end end end diff --git a/spec/ruby/optional/capi/rbasic_spec.rb b/spec/ruby/optional/capi/rbasic_spec.rb index 283e70db70..7b5b5b2fed 100644 --- a/spec/ruby/optional/capi/rbasic_spec.rb +++ b/spec/ruby/optional/capi/rbasic_spec.rb @@ -1,7 +1,9 @@ require_relative 'spec_helper' require_relative 'shared/rbasic' load_extension("rbasic") -load_extension("data") +ruby_version_is ""..."3.4" do + load_extension("data") +end load_extension("array") describe "RBasic support for regular objects" do @@ -12,11 +14,35 @@ describe "RBasic support for regular objects" do it_should_behave_like :rbasic end -describe "RBasic support for RData" do - before :all do - @specs = CApiRBasicRDataSpecs.new - @wrapping = CApiWrappedStructSpecs.new - @data = -> { [@wrapping.wrap_struct(1024), @wrapping.wrap_struct(1025)] } +ruby_version_is ""..."3.4" do + describe "RBasic support for RData" do + before :all do + @specs = CApiRBasicRDataSpecs.new + @wrapping = CApiWrappedStructSpecs.new + @data = -> { [@wrapping.wrap_struct(1024), @wrapping.wrap_struct(1025)] } + end + it_should_behave_like :rbasic + + it "supports user flags" do + obj, _ = @data.call + initial = @specs.get_flags(obj) + @specs.set_flags(obj, 1 << 14 | 1 << 16 | initial).should == 1 << 14 | 1 << 16 | initial + @specs.get_flags(obj).should == 1 << 14 | 1 << 16 | initial + @specs.set_flags(obj, initial).should == initial + end + + it "supports copying the flags from one object over to the other" do + obj1, obj2 = @data.call + initial = @specs.get_flags(obj1) + @specs.get_flags(obj2).should == initial + @specs.set_flags(obj1, 1 << 14 | 1 << 16 | initial) + @specs.get_flags(obj1).should == 1 << 14 | 1 << 16 | initial + + @specs.copy_flags(obj2, obj1) + @specs.get_flags(obj2).should == 1 << 14 | 1 << 16 | initial + @specs.set_flags(obj1, initial) + @specs.copy_flags(obj2, obj1) + @specs.get_flags(obj2).should == initial + end end - it_should_behave_like :rbasic end diff --git a/spec/ruby/optional/capi/regexp_spec.rb b/spec/ruby/optional/capi/regexp_spec.rb index 806157368d..f233b5e3b3 100644 --- a/spec/ruby/optional/capi/regexp_spec.rb +++ b/spec/ruby/optional/capi/regexp_spec.rb @@ -77,7 +77,52 @@ describe "C-API Regexp function" do end it "returns MatchData when used with rb_reg_match" do - @p.rb_reg_match_backref_get(/a/, 'ab')[0].should == 'a' + @p.rb_reg_match_backref_get(/a/, 'ab')[0].should == 'a' + end + end + + describe "rb_backref_set" do + before :each do + @md = "foo".match(/foo/) + $~ = nil + end + + it "sets the value of $~" do + @p.rb_backref_set(@md) + @p.rb_backref_get.should == @md + $~.should == @md + end + + it "sets a Thread-local value" do + running = false + + thr = Thread.new do + @p.rb_backref_set(@md) + @p.rb_backref_get.should == @md + $~.should == @md + running = true + end + + Thread.pass while thr.status and !running + $~.should == nil + + thr.join + end + end + + describe "rb_memcicmp" do + it "returns 0 for identical strings" do + @p.rb_memcicmp('Hello', 'Hello').should == 0 + end + + it "returns 0 for strings which only differ in case" do + @p.rb_memcicmp('Hello', 'HELLO').should == 0 + @p.rb_memcicmp('HELLO', 'Hello').should == 0 + end + + it "returns the difference between the first non matching characters" do + @p.rb_memcicmp('Hello', 'HELLP').should == -1 + @p.rb_memcicmp('HELLp', 'Hello').should == 1 end end end diff --git a/spec/ruby/optional/capi/set_spec.rb b/spec/ruby/optional/capi/set_spec.rb new file mode 100644 index 0000000000..21a9756236 --- /dev/null +++ b/spec/ruby/optional/capi/set_spec.rb @@ -0,0 +1,96 @@ +require_relative 'spec_helper' + +ruby_version_is "4.0" do + load_extension("set") + + describe "C-API Set function" do + before :each do + @s = CApiSetSpecs.new + end + + describe "rb_set_foreach" do + it "calls function with each element and arg" do + a = [] + @s.rb_set_foreach(Set[1, 2], 3) {|*args| a.concat(args) } + a.should == [1, 3, 2, 3] + end + + it "respects function return value" do + a = [] + @s.rb_set_foreach(Set[1, 2], 3) do |*args| + a.concat(args) + false + end + a.should == [1, 3] + end + end + + describe "rb_set_new" do + it "returns a new set" do + @s.rb_set_new.should == Set[] + end + end + + describe "rb_set_new_capa" do + it "returns a new set" do + @s.rb_set_new_capa(3).should == Set[] + end + end + + describe "rb_set_lookup" do + it "returns whether the element is in the set" do + set = Set[1] + @s.rb_set_lookup(set, 1).should == true + @s.rb_set_lookup(set, 2).should == false + end + end + + describe "rb_set_add" do + it "adds element to set" do + set = Set[] + @s.rb_set_add(set, 1).should == true + set.should == Set[1] + @s.rb_set_add(set, 2).should == true + set.should == Set[1, 2] + end + + it "returns false if element is already in set" do + set = Set[1] + @s.rb_set_add(set, 1).should == false + set.should == Set[1] + end + end + + describe "rb_set_clear" do + it "empties and returns self" do + set = Set[1] + @s.rb_set_clear(set).should.equal?(set) + set.should == Set[] + end + end + + describe "rb_set_delete" do + it "removes element from set" do + set = Set[1, 2] + @s.rb_set_delete(set, 1).should == true + set.should == Set[2] + @s.rb_set_delete(set, 2).should == true + set.should == Set[] + end + + it "returns false if element is not already in set" do + set = Set[2] + @s.rb_set_delete(set, 1).should == false + set.should == Set[2] + end + end + + describe "rb_set_size" do + it "returns number of elements in set" do + @s.rb_set_size(Set[]).should == 0 + @s.rb_set_size(Set[1]).should == 1 + @s.rb_set_size(Set[1,2]).should == 2 + end + end + end +end diff --git a/spec/ruby/optional/capi/shared/rbasic.rb b/spec/ruby/optional/capi/shared/rbasic.rb index f202b72f33..e3485d4b7b 100644 --- a/spec/ruby/optional/capi/shared/rbasic.rb +++ b/spec/ruby/optional/capi/shared/rbasic.rb @@ -1,8 +1,6 @@ describe :rbasic, shared: true do - before :all do specs = CApiRBasicSpecs.new - @taint = specs.taint_flag @freeze = specs.freeze_flag end @@ -10,7 +8,7 @@ describe :rbasic, shared: true do obj, _ = @data.call initial = @specs.get_flags(obj) obj.freeze - @specs.get_flags(obj).should == @freeze | initial + (@specs.get_flags(obj) & 0xFFFF).should == (@freeze | initial) & 0xFFFF end it "supports setting the FREEZE flag" do @@ -20,60 +18,6 @@ describe :rbasic, shared: true do obj.should.frozen? end - ruby_version_is ""..."2.7" do - it "reports the appropriate FREEZE and TAINT flags for the object when reading" do - obj, _ = @data.call - initial = @specs.get_flags(obj) - obj.taint - @specs.get_flags(obj).should == @taint | initial - obj.untaint - @specs.get_flags(obj).should == initial - obj.freeze - @specs.get_flags(obj).should == @freeze | initial - - obj, _ = @data.call - obj.taint - obj.freeze - @specs.get_flags(obj).should == @freeze | @taint | initial - end - - it "supports setting the FREEZE and TAINT flags" do - obj, _ = @data.call - initial = @specs.get_flags(obj) - @specs.set_flags(obj, @taint | initial).should == @taint | initial - obj.should.tainted? - @specs.set_flags(obj, initial).should == initial - obj.should_not.tainted? - @specs.set_flags(obj, @freeze | initial).should == @freeze | initial - obj.should.frozen? - - obj, _ = @data.call - @specs.set_flags(obj, @freeze | @taint | initial).should == @freeze | @taint | initial - obj.should.tainted? - obj.should.frozen? - end - end - - it "supports user flags" do - obj, _ = @data.call - initial = @specs.get_flags(obj) - @specs.set_flags(obj, 1 << 14 | 1 << 16 | initial).should == 1 << 14 | 1 << 16 | initial - @specs.get_flags(obj).should == 1 << 14 | 1 << 16 | initial - @specs.set_flags(obj, initial).should == initial - end - - it "supports copying the flags from one object over to the other" do - obj1, obj2 = @data.call - initial = @specs.get_flags(obj1) - @specs.get_flags(obj2).should == initial - @specs.set_flags(obj1, 1 << 14 | 1 << 16 | initial) - @specs.copy_flags(obj2, obj1) - @specs.get_flags(obj2).should == 1 << 14 | 1 << 16 | initial - @specs.set_flags(obj1, initial) - @specs.copy_flags(obj2, obj1) - @specs.get_flags(obj2).should == initial - end - it "supports retrieving the (meta)class" do obj, _ = @data.call @specs.get_klass(obj).should == obj.class diff --git a/spec/ruby/optional/capi/spec_helper.rb b/spec/ruby/optional/capi/spec_helper.rb index 2c36ead8d0..49ce23d874 100644 --- a/spec/ruby/optional/capi/spec_helper.rb +++ b/spec/ruby/optional/capi/spec_helper.rb @@ -29,12 +29,14 @@ def compile_extension(name) ext = "#{name}_spec" lib = "#{object_path}/#{ext}.#{RbConfig::CONFIG['DLEXT']}" - ruby_header = "#{RbConfig::CONFIG['rubyhdrdir']}/ruby.h" + rubyhdrdir = RbConfig::CONFIG['rubyhdrdir'] + ruby_header = "#{rubyhdrdir}/ruby.h" + abi_header = "#{rubyhdrdir}/ruby/internal/abi.h" if RbConfig::CONFIG["ENABLE_SHARED"] == "yes" - libdirname = RbConfig::CONFIG['LIBPATHENV'] == 'PATH' ? 'bindir' : - RbConfig::CONFIG['libdirname'] # defined since 2.1 - libruby_so = "#{RbConfig::CONFIG[libdirname]}/#{RbConfig::CONFIG['LIBRUBY_SO']}" + # below is defined since 2.1, except for mswin, and maybe other platforms + libdirname = RbConfig::CONFIG.fetch 'libdirname', 'libdir' + libruby = "#{RbConfig::CONFIG[libdirname]}/#{RbConfig::CONFIG['LIBRUBY']}" end begin @@ -46,7 +48,8 @@ def compile_extension(name) when mtime <= File.mtime("#{core_ext_dir}/rubyspec.h") when mtime <= File.mtime("#{spec_ext_dir}/#{ext}.c") when mtime <= File.mtime(ruby_header) - when libruby_so && mtime <= File.mtime(libruby_so) + when (mtime <= File.mtime(abi_header) rescue nil) + when libruby && mtime <= File.mtime(libruby) else return lib # up-to-date end @@ -56,7 +59,11 @@ def compile_extension(name) tmpdir = tmp("cext_#{name}") Dir.mkdir(tmpdir) begin - ["#{core_ext_dir}/rubyspec.h", "#{spec_ext_dir}/#{ext}.c"].each do |file| + files = ["#{core_ext_dir}/rubyspec.h", "#{spec_ext_dir}/#{ext}.c"] + if spec_ext_dir != core_ext_dir + files += Dir.glob("#{spec_ext_dir}/*.h") + end + files.each do |file| if cxx and file.end_with?('.c') cp file, "#{tmpdir}/#{File.basename(file, '.c')}.cpp" else @@ -71,12 +78,25 @@ def compile_extension(name) init_mkmf unless required create_makefile(ext, tmpdir) else + # Workaround for digest C-API specs to find the ruby/digest.h header + # when run in the CRuby repository via make test-spec + if MSpecScript.instance_variable_defined?(:@testing_ruby) + ruby_repository_extra_include_dir = "-I#{RbConfig::CONFIG.fetch("prefix")}/#{RbConfig::CONFIG.fetch("EXTOUT")}/include" + end + File.write("extconf.rb", <<-RUBY) require 'mkmf' $ruby = ENV.values_at('RUBY_EXE', 'RUBY_FLAGS').join(' ') # MRI magic to consider building non-bundled extensions $extout = nil + if RbConfig::CONFIG.key?("buildlibdir") # The top directory where the libruby is built. + # Prepend the dummy macro to bypass the conversion in `with_destdir` on DOSISH + # platforms, where the drive letter is replaced with `$(DESTDIR)`. `DESTDIR` is + # overridden by the command line argument bellow. + RbConfig::MAKEFILE_CONFIG["buildlibdir"] = "$(empty)" + RbConfig::CONFIG["buildlibdir"] + end append_cflags '-Wno-declaration-after-statement' + #{"append_cflags #{ruby_repository_extra_include_dir.inspect}" if ruby_repository_extra_include_dir} create_makefile(#{ext.inspect}) RUBY output = ruby_exe("extconf.rb") @@ -86,7 +106,7 @@ def compile_extension(name) # Do not capture stderr as we want to show compiler warnings make, opts = setup_make - output = IO.popen([make, "V=1", "DESTDIR=", opts], &:read) + output = IO.popen([*make, "V=1", "DESTDIR=", opts], &:read) raise "#{make} failed:\n#{output}" unless $?.success? $stderr.puts output if debug @@ -103,26 +123,34 @@ end def setup_make make = ENV['MAKE'] make ||= (RbConfig::CONFIG['host_os'].include?("mswin") ? "nmake" : "make") - make_flags = ENV["MAKEFLAGS"] || '' + env = %w[MFLAGS MAKEFLAGS GNUMAKEFLAGS].to_h {|var| [var, ENV[var]]} + make_flags = env["MAKEFLAGS"] || '' # suppress logo of nmake.exe to stderr if File.basename(make, ".*").downcase == "nmake" and !make_flags.include?("l") - ENV["MAKEFLAGS"] = "l#{make_flags}" + env["MAKEFLAGS"] = "l#{make_flags}" end opts = {} if /(?:\A|\s)--jobserver-(?:auth|fds)=(\d+),(\d+)/ =~ make_flags - begin - r = IO.for_fd($1.to_i(10), "rb", autoclose: false) - w = IO.for_fd($2.to_i(10), "wb", autoclose: false) - rescue Errno::EBADF - else - opts[r] = r - opts[w] = w + [$1, $2].each do |fd| + fd = IO.for_fd(fd.to_i(10), autoclose: false) + opts[fd] = fd + rescue + # Jobserver is not usable, maybe no `+` flag or on Windows. + job_options = /\A\s*(?:-j\d+\s*|-\S+\Kj\d+)|(?:\G|\s)\K--jobserver-(?:auth|fds)=\S+\s*/ + env["MAKEFLAGS"] = make_flags.gsub(job_options, '') + %w[GNUMAKEFLAGS MFLAGS].each do |var| + if flags = env[var] + env[var] = flags.gsub(job_options, '') + end + end + opts.clear + break end end - [make, opts] + [[env, make], opts] end def load_extension(name) diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb index 7c33b79348..3e095f05c8 100644 --- a/spec/ruby/optional/capi/string_spec.rb +++ b/spec/ruby/optional/capi/string_spec.rb @@ -1,4 +1,5 @@ # encoding: utf-8 +# frozen_string_literal: false require_relative 'spec_helper' require_relative '../../shared/string/times' @@ -47,7 +48,7 @@ describe "C-API String function" do [Encoding::BINARY, Encoding::UTF_8].each do |enc| describe "rb_str_set_len on a #{enc.name} String" do before :each do - @str = "abcdefghij".force_encoding(enc) + @str = "abcdefghij".dup.force_encoding(enc) # Make sure to unshare the string @s.rb_str_modify(@str) end @@ -97,6 +98,32 @@ describe "C-API String function" do end end + describe "rb_str_set_len on a UTF-16 String" do + before :each do + @str = "abcdefghij".dup.force_encoding(Encoding::UTF_16BE) + # Make sure to unshare the string + @s.rb_str_modify(@str) + end + + it "inserts two NULL bytes at the length" do + @s.rb_str_set_len(@str, 4).b.should == "abcd".b + @s.rb_str_set_len(@str, 8).b.should == "abcd\x00\x00gh".b + end + end + + describe "rb_str_set_len on a UTF-32 String" do + before :each do + @str = "abcdefghijkl".dup.force_encoding(Encoding::UTF_32BE) + # Make sure to unshare the string + @s.rb_str_modify(@str) + end + + it "inserts four NULL bytes at the length" do + @s.rb_str_set_len(@str, 4).b.should == "abcd".b + @s.rb_str_set_len(@str, 12).b.should == "abcd\x00\x00\x00\x00ijkl".b + end + end + describe "rb_str_buf_new" do it "returns the equivalent of an empty string" do buf = @s.rb_str_buf_new(10, nil) @@ -108,7 +135,7 @@ describe "C-API String function" do it "returns a string with the given capacity" do buf = @s.rb_str_buf_new(256, nil) - @s.rb_str_capacity(buf).should == 256 + @s.rb_str_capacity(buf).should >= 256 end it "returns a string that can be appended to" do @@ -164,11 +191,19 @@ describe "C-API String function" do end it "returns a new String object filled with \\0 bytes" do - s = @s.rb_str_tmp_new(4) - s.encoding.should == Encoding::BINARY - s.bytesize.should == 4 - s.size.should == 4 - s.should == "\x00\x00\x00\x00" + lens = [4] + + ruby_version_is "4.0" do + lens << 100 + end + + lens.each do |len| + s = @s.rb_str_tmp_new(len) + s.encoding.should == Encoding::BINARY + s.bytesize.should == len + s.size.should == len + s.should == "\x00" * len + end end end @@ -181,12 +216,6 @@ describe "C-API String function" do @s.rb_str_new("hello", 3).should == "hel" end - ruby_version_is ''...'2.7' do - it "returns a non-tainted string" do - @s.rb_str_new("hello", 5).should_not.tainted? - end - end - it "returns an empty string if len is 0" do @s.rb_str_new("hello", 0).should == "" end @@ -211,7 +240,7 @@ describe "C-API String function" do describe "rb_usascii_str_new" do it "creates a new String with US-ASCII Encoding from a char buffer of len characters" do - str = "abc".force_encoding("us-ascii") + str = "abc".dup.force_encoding("us-ascii") result = @s.rb_usascii_str_new("abcdef", 3) result.should == str result.encoding.should == Encoding::US_ASCII @@ -227,14 +256,14 @@ describe "C-API String function" do it "returns US-ASCII string for non-US-ASCII string literal" do str = @s.rb_usascii_str_new_lit_non_ascii - str.should == "r\xC3\xA9sum\xC3\xA9".force_encoding(Encoding::US_ASCII) + str.should == "r\xC3\xA9sum\xC3\xA9".dup.force_encoding(Encoding::US_ASCII) str.encoding.should == Encoding::US_ASCII end end describe "rb_usascii_str_new_cstr" do it "creates a new String with US-ASCII Encoding" do - str = "abc".force_encoding("us-ascii") + str = "abc".dup.force_encoding("us-ascii") result = @s.rb_usascii_str_new_cstr("abc") result.should == str result.encoding.should == Encoding::US_ASCII @@ -257,13 +286,13 @@ describe "C-API String function" do it "returns a dup of the original String" do a = "abc" b = @s.rb_str_encode("abc", "us-ascii", 0, nil) - a.should_not equal(b) + a.should_not.equal?(b) end it "returns a duplicate of the original when the encoding doesn't change" do a = "abc" b = @s.rb_str_encode("abc", Encoding::UTF_8, 0, nil) - a.should_not equal(b) + a.should_not.equal?(b) end it "accepts encoding flags" do @@ -291,7 +320,7 @@ describe "C-API String function" do str1 = "hi" str2 = @s.rb_str_new3 str1 str1.should == str2 - str1.should_not equal str2 + str1.should_not.equal? str2 end end @@ -301,7 +330,7 @@ describe "C-API String function" do str1.freeze str2 = @s.rb_str_new4 str1 str1.should == str2 - str1.should equal(str2) + str1.should.equal?(str2) str1.should.frozen? str2.should.frozen? end @@ -310,7 +339,7 @@ describe "C-API String function" do str1 = "hi" str2 = @s.rb_str_new4 str1 str1.should == str2 - str1.should_not equal(str2) + str1.should_not.equal?(str2) str2.should.frozen? end end @@ -320,7 +349,7 @@ describe "C-API String function" do str1 = "hi" str2 = @s.rb_str_dup str1 str1.should == str2 - str1.should_not equal str2 + str1.should_not.equal? str2 end end @@ -335,31 +364,13 @@ describe "C-API String function" do end end - ruby_version_is ''...'2.7' do - describe "rb_tainted_str_new" do - it "creates a new tainted String" do - newstring = @s.rb_tainted_str_new("test", 4) - newstring.should == "test" - newstring.tainted?.should be_true - end - end - - describe "rb_tainted_str_new2" do - it "creates a new tainted String" do - newstring = @s.rb_tainted_str_new2("test") - newstring.should == "test" - newstring.tainted?.should be_true - end - end - end - describe "rb_str_append" do it "appends a string to another string" do @s.rb_str_append("Hello", " Goodbye").should == "Hello Goodbye" end it "raises a TypeError trying to append non-String-like object" do - -> { @s.rb_str_append("Hello", 32323)}.should raise_error(TypeError) + -> { @s.rb_str_append("Hello", 32323)}.should.raise(TypeError) end it "changes Encoding if a string is appended to an empty string" do @@ -378,6 +389,14 @@ describe "C-API String function" do it_behaves_like :string_times, :rb_str_times, -> str, times { @s.rb_str_times(str, times) } end + describe "rb_str_buf_append" do + it "concatenates a string to another string" do + str = "Your house " + @s.rb_str_buf_append(str, "is on fire?").should.equal?(str) + str.should == "Your house is on fire?" + end + end + describe "rb_str_buf_cat" do it "concatenates a C string to a ruby string" do @s.rb_str_buf_cat("Your house is on fire").should == "Your house is on fire?" @@ -408,7 +427,7 @@ describe "C-API String function" do describe "rb_enc_str_buf_cat" do it "concatenates a C string literal to a ruby string with the given encoding" do - input = "hello ".force_encoding(Encoding::US_ASCII) + input = "hello ".dup.force_encoding(Encoding::US_ASCII) result = @s.rb_enc_str_buf_cat(input, "résumé", Encoding::UTF_8) result.should == "hello résumé" result.encoding.should == Encoding::UTF_8 @@ -438,6 +457,20 @@ describe "C-API String function" do end end + describe "rb_str_strlen" do + it 'returns 0 as the length of an empty string' do + @s.rb_str_strlen('').should == 0 + end + + it 'returns the number of characters in a string' do + @s.rb_str_strlen('hello').should == 5 + end + + it 'returns the number of characters in a string with multi-byte characters' do + @s.rb_str_strlen('こんにちは').should == 5 + end + end + describe "rb_str_split" do it "splits strings over a splitter" do @s.rb_str_split("Hello,Goodbye").should == ["Hello", "Goodbye"] @@ -466,7 +499,7 @@ describe "C-API String function" do end it "converts a C string to a Fixnum strictly if base is 0" do - -> { @s.rb_cstr2inum("1234a", 0) }.should raise_error(ArgumentError) + -> { @s.rb_cstr2inum("1234a", 0) }.should.raise(ArgumentError) end end @@ -484,33 +517,14 @@ describe "C-API String function" do end it "converts a C string to a Fixnum strictly" do - -> { @s.rb_cstr_to_inum("1234a", 10, true) }.should raise_error(ArgumentError) - end - end - - describe "rb_fstring" do - it 'returns self if the String is frozen' do - input = 'foo'.freeze - output = @s.rb_fstring(input) - - output.should equal(input) - output.should.frozen? - end - - it 'returns a frozen copy if the String is not frozen' do - input = 'foo' - output = @s.rb_fstring(input) - - output.should.frozen? - output.should_not equal(input) - output.should == 'foo' + -> { @s.rb_cstr_to_inum("1234a", 10, true) }.should.raise(ArgumentError) end end describe "rb_str_subseq" do it "returns a byte-indexed substring" do - str = "\x00\x01\x02\x03\x04".force_encoding("binary") - @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".force_encoding("binary") + str = "\x00\x01\x02\x03\x04".dup.force_encoding("binary") + @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".dup.force_encoding("binary") end end @@ -522,6 +536,61 @@ describe "C-API String function" do end end + describe "rb_str_sublen" do + it "returns the character length for a given byte offset in an ASCII string" do + @s.rb_str_sublen("hello", 3).should == 3 + end + + it "returns the character length for a given byte offset in a multibyte string" do + # "hëllo" where 'ë' is 2 bytes in UTF-8, total 6 bytes + str = "hëllo" + @s.rb_str_sublen(str, 3).should == 2 + end + + it "returns 0 for byte offset 0" do + @s.rb_str_sublen("hello", 0).should == 0 + end + + it "returns the full character length for the total byte length" do + str = "hëllo" + @s.rb_str_sublen(str, str.bytesize).should == str.length + end + end + + describe "rb_str_subpos" do + it "returns [byte_offset, byte_length] for a valid character offset in an ASCII string" do + @s.rb_str_subpos("hello", 1).should == [1, 4] + end + + it "returns [byte_offset, byte_length] for a valid character offset in a multibyte string" do + # "hëllo" where 'ë' is 2 bytes in UTF-8 + str = "hëllo" + @s.rb_str_subpos(str, 0).should == [0, 6] + @s.rb_str_subpos(str, 1).should == [1, 5] + @s.rb_str_subpos(str, 2).should == [3, 3] + end + + it "returns [0, byte_length] for offset 0" do + @s.rb_str_subpos("hello", 0).should == [0, 5] + end + + it "returns nil for a negative offset that is out of range" do + @s.rb_str_subpos("hello", -6).should == nil + end + + it "returns the correct position for a negative offset" do + @s.rb_str_subpos("hello", -2).should == [3, 2] + end + + it "returns [byte_length, 0] when offset equals string length" do + @s.rb_str_subpos("hello", 5).should == [5, 0] + end + + it "returns nil when offset is beyond string length" do + @s.rb_str_subpos("hello", 6).should == nil + end + end + describe "rb_str_to_str" do it "calls #to_str to coerce the value to a String" do @s.rb_str_to_str("foo").should == "foo" @@ -529,8 +598,8 @@ describe "C-API String function" do end it "raises a TypeError if coercion fails" do - -> { @s.rb_str_to_str(0) }.should raise_error(TypeError) - -> { @s.rb_str_to_str(CApiStringSpecs::InvalidTostrTest.new) }.should raise_error(TypeError) + -> { @s.rb_str_to_str(0) }.should.raise(TypeError) + -> { @s.rb_str_to_str(CApiStringSpecs::InvalidTostrTest.new) }.should.raise(TypeError) end end @@ -598,6 +667,22 @@ describe "C-API String function" do str = " " @s.RSTRING_PTR_short_memcpy(str).should == "Infinity" end + + it "allows read() to update the string contents" do + filename = fixture(__FILE__, "read.txt") + str = "" + capacities = @s.RSTRING_PTR_read(str, filename) + capacities[0].should >= 30 + capacities[1].should >= 53 + capacities[0].should < capacities[1] + str.should == "fixture file contents to test read() with RSTRING_PTR" + end + + it "terminates the string with at least (encoding min length) \\0 bytes" do + @s.RSTRING_PTR_null_terminate("abc", 1).should == "\x00" + @s.RSTRING_PTR_null_terminate("abc".encode("UTF-16BE"), 2).should == "\x00\x00" + @s.RSTRING_PTR_null_terminate("abc".encode("UTF-32BE"), 4).should == "\x00\x00\x00\x00" + end end describe "RSTRING_LEN" do @@ -638,7 +723,7 @@ describe "C-API String function" do it "does not call #to_s on non-String objects" do str = mock("fake") str.should_not_receive(:to_s) - -> { @s.send(@method, str) }.should raise_error(TypeError) + -> { @s.send(@method, str) }.should.raise(TypeError) end end @@ -647,43 +732,42 @@ describe "C-API String function" do end describe "SafeStringValue" do - ruby_version_is ''...'2.7' do - it "raises for tained string when $SAFE is 1" do - begin - Thread.new { - $SAFE = 1 - -> { - @s.SafeStringValue("str".taint) - }.should raise_error(SecurityError) - }.join - ensure - $SAFE = 0 - end - end + end - it_behaves_like :string_value_macro, :SafeStringValue + describe "rb_str_modify" do + it "raises an error if the string is frozen" do + -> { @s.rb_str_modify("frozen".freeze) }.should.raise(FrozenError) end end describe "rb_str_modify_expand" do it "grows the capacity to bytesize + expand, not changing the bytesize" do str = @s.rb_str_buf_new(256, "abcd") - @s.rb_str_capacity(str).should == 256 + @s.rb_str_capacity(str).should >= 256 @s.rb_str_set_len(str, 3) str.bytesize.should == 3 @s.RSTRING_LEN(str).should == 3 - @s.rb_str_capacity(str).should == 256 + @s.rb_str_capacity(str).should >= 256 @s.rb_str_modify_expand(str, 4) str.bytesize.should == 3 @s.RSTRING_LEN(str).should == 3 - @s.rb_str_capacity(str).should == 7 + @s.rb_str_capacity(str).should >= 7 @s.rb_str_modify_expand(str, 1024) str.bytesize.should == 3 @s.RSTRING_LEN(str).should == 3 - @s.rb_str_capacity(str).should == 1027 + @s.rb_str_capacity(str).should >= 1027 + + @s.rb_str_modify_expand(str, 1) + str.bytesize.should == 3 + @s.RSTRING_LEN(str).should == 3 + @s.rb_str_capacity(str).should >= 4 + end + + it "raises an error if the string is frozen" do + -> { @s.rb_str_modify_expand("frozen".freeze, 10) }.should.raise(FrozenError) end end @@ -700,8 +784,13 @@ describe "C-API String function" do @s.rb_str_resize_RSTRING_LEN("test", 2).should == 2 end + it "copies the existing bytes" do + str = "t" + @s.rb_str_resize_copy(str).should == "test" + end + it "increases the size of the string" do - expected = "test".force_encoding("US-ASCII") + expected = "test".dup.force_encoding("US-ASCII") str = @s.rb_str_resize(expected.dup, 12) str.size.should == 12 str.bytesize.should == 12 @@ -734,14 +823,14 @@ describe "C-API String function" do it "freezes the string" do s = "" @s.rb_str_freeze(s).should == s - s.frozen?.should be_true + s.frozen?.should == true end end describe "rb_str_hash" do it "hashes the string into a number" do s = "hello" - @s.rb_str_hash(s).should be_kind_of(Integer) + @s.rb_str_hash(s).should.is_a?(Integer) end end @@ -757,7 +846,7 @@ describe "rb_str_free" do # is available. There is no guarantee this even does # anything at all it "indicates data for a string might be freed" do - @s.rb_str_free("xyz").should be_nil + @s.rb_str_free("xyz").should == nil end end @@ -772,12 +861,6 @@ describe :rb_external_str_new, shared: true do x80 = [0x80].pack('C') @s.send(@method, "#{x80}abc").encoding.should == Encoding::BINARY end - - ruby_version_is ''...'2.7' do - it "returns a tainted String" do - @s.send(@method, "abc").tainted?.should be_true - end - end end describe "C-API String function" do @@ -805,12 +888,12 @@ describe "C-API String function" do describe "rb_str_equal" do it "compares two same strings" do s = "hello" - @s.rb_str_equal(s, "hello").should be_true + @s.rb_str_equal(s, "hello").should == true end it "compares two different strings" do s = "hello" - @s.rb_str_equal(s, "hella").should be_false + @s.rb_str_equal(s, "hella").should == false end end @@ -838,14 +921,14 @@ describe "C-API String function" do # it "transcodes a String to Encoding.default_internal if it is set" do # Encoding.default_internal = Encoding::EUC_JP # -# - a = "\xE3\x81\x82\xe3\x82\x8c".force_encoding("utf-8") +# - a = "\xE3\x81\x82\xe3\x82\x8c".dup.force_encoding("utf-8") # + a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8") # s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8) # - -# - s.should == "\xA4\xA2\xA4\xEC".force_encoding("euc-jp") +# - s.should == "\xA4\xA2\xA4\xEC".dup.force_encoding("euc-jp") # + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4')#.force_encoding('binary') # + s.should == x -# s.encoding.should equal(Encoding::EUC_JP) +# s.encoding.should.equal?(Encoding::EUC_JP) # end it "transcodes a String to Encoding.default_internal if it is set" do @@ -855,107 +938,104 @@ describe "C-API String function" do s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8) x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('euc-jp') s.should == x - s.encoding.should equal(Encoding::EUC_JP) - end - - ruby_version_is ''...'2.7' do - it "returns a tainted String" do - s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::US_ASCII) - s.tainted?.should be_true - end + s.encoding.should.equal?(Encoding::EUC_JP) end end describe "rb_locale_str_new" do it "returns a String with 'locale' encoding" do s = @s.rb_locale_str_new("abc", 3) - s.should == "abc".force_encoding(Encoding.find("locale")) - s.encoding.should equal(Encoding.find("locale")) + s.should == "abc".dup.force_encoding(Encoding.find("locale")) + s.encoding.should.equal?(Encoding.find("locale")) end end describe "rb_locale_str_new_cstr" do it "returns a String with 'locale' encoding" do s = @s.rb_locale_str_new_cstr("abc") - s.should == "abc".force_encoding(Encoding.find("locale")) - s.encoding.should equal(Encoding.find("locale")) + s.should == "abc".dup.force_encoding(Encoding.find("locale")) + s.encoding.should.equal?(Encoding.find("locale")) end end describe "rb_str_conv_enc" do it "returns the original String when to encoding is not specified" do - a = "abc".force_encoding("us-ascii") - @s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should equal(a) + a = "abc".dup.force_encoding("us-ascii") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should.equal?(a) end it "returns the original String if a transcoding error occurs" do - a = [0xEE].pack('C').force_encoding("utf-8") - @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should equal(a) + a = [0xEE].pack('C').force_encoding(Encoding::UTF_8) + @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should.equal?(a) + a.encoding.should == Encoding::UTF_8 + + a = "\x80".b + @s.rb_str_conv_enc(a, Encoding::BINARY, Encoding::UTF_8).should.equal?(a) + a.encoding.should == Encoding::BINARY end it "returns a transcoded String" do - a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8") + a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding(Encoding::UTF_8) result = @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP) - x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8') - result.should == x.force_encoding("euc-jp") - result.encoding.should equal(Encoding::EUC_JP) + result.should == [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding(Encoding::EUC_JP) + result.encoding.should == Encoding::EUC_JP end describe "when the String encoding is equal to the destination encoding" do it "returns the original String" do - a = "abc".force_encoding("us-ascii") - @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should equal(a) + a = "abc".dup.force_encoding("us-ascii") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should.equal?(a) end it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do a = "abc".encode("us-ascii") - @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::US_ASCII).should equal(a) + @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::US_ASCII).should.equal?(a) end it "returns the origin String if the destination encoding is BINARY" do - a = "abc".force_encoding("binary") - @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::BINARY).should equal(a) + a = "abc".dup.force_encoding("binary") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::BINARY).should.equal?(a) end end end describe "rb_str_conv_enc_opts" do it "returns the original String when to encoding is not specified" do - a = "abc".force_encoding("us-ascii") - @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should equal(a) + a = "abc".dup.force_encoding("us-ascii") + @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should.equal?(a) end it "returns the original String if a transcoding error occurs" do a = [0xEE].pack('C').force_encoding("utf-8") @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, - Encoding::EUC_JP, 0, nil).should equal(a) + Encoding::EUC_JP, 0, nil).should.equal?(a) end it "returns a transcoded String" do - a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8") + a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding("utf-8") result = @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, Encoding::EUC_JP, 0, nil) x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8') result.should == x.force_encoding("euc-jp") - result.encoding.should equal(Encoding::EUC_JP) + result.encoding.should.equal?(Encoding::EUC_JP) end describe "when the String encoding is equal to the destination encoding" do it "returns the original String" do - a = "abc".force_encoding("us-ascii") + a = "abc".dup.force_encoding("us-ascii") @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, - Encoding::US_ASCII, 0, nil).should equal(a) + Encoding::US_ASCII, 0, nil).should.equal?(a) end it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do a = "abc".encode("us-ascii") @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, - Encoding::US_ASCII, 0, nil).should equal(a) + Encoding::US_ASCII, 0, nil).should.equal?(a) end it "returns the origin String if the destination encoding is BINARY" do - a = "abc".force_encoding("binary") + a = "abc".dup.force_encoding("binary") @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, - Encoding::BINARY, 0, nil).should equal(a) + Encoding::BINARY, 0, nil).should.equal?(a) end end end @@ -964,15 +1044,15 @@ describe "C-API String function" do it "returns the original String with the external encoding" do Encoding.default_external = Encoding::ISO_8859_1 s = @s.rb_str_export("Hëllo") - s.encoding.should equal(Encoding::ISO_8859_1) + s.encoding.should.equal?(Encoding::ISO_8859_1) end end describe "rb_str_export_locale" do it "returns the original String with the locale encoding" do s = @s.rb_str_export_locale("abc") - s.should == "abc".force_encoding(Encoding.find("locale")) - s.encoding.should equal(Encoding.find("locale")) + s.should == "abc".dup.force_encoding(Encoding.find("locale")) + s.encoding.should.equal?(Encoding.find("locale")) end end @@ -987,7 +1067,7 @@ describe "C-API String function" do it "returns the source string if it can not be converted" do source = ["00ff"].pack("H*"); result = @s.rb_str_export_to_enc(source, Encoding::UTF_8) - result.should equal(source) + result.should.equal?(source) end it "does not alter the source string if it can not be converted" do @@ -995,7 +1075,7 @@ describe "C-API String function" do result = @s.rb_str_export_to_enc(source, Encoding::UTF_8) source.bytes.should == [0, 255] end -end + end describe "rb_sprintf" do it "replaces the parts like sprintf" do @@ -1024,9 +1104,64 @@ end end it "formats a TrueClass VALUE as 'true' if sign specified in format" do - s = 'Result: true.' + s = 'Result: TrueClass.' @s.rb_sprintf4(true.class).should == s end + + it "formats nil using to_s if sign not specified in format" do + s = 'Result: .' + @s.rb_sprintf3(nil).should == s + end + + it "formats nil using inspect if sign specified in format" do + s = 'Result: nil.' + @s.rb_sprintf4(nil).should == s + end + + it "truncates a string to a supplied precision if that is shorter than the string" do + s = 'Result: Hel.' + @s.rb_sprintf5(0, 3, "Hello").should == s + end + + it "does not truncates a string to a supplied precision if that is longer than the string" do + s = 'Result: Hello.' + @s.rb_sprintf5(0, 8, "Hello").should == s + end + + it "pads a string to a supplied width if that is longer than the string" do + s = 'Result: Hello.' + @s.rb_sprintf5(8, 5, "Hello").should == s + end + + it "truncates a VALUE string to a supplied precision if that is shorter than the VALUE string" do + s = 'Result: Hel.' + @s.rb_sprintf6(0, 3, "Hello").should == s + end + + it "does not truncates a VALUE string to a supplied precision if that is longer than the VALUE string" do + s = 'Result: Hello.' + @s.rb_sprintf6(0, 8, "Hello").should == s + end + + it "pads a VALUE string to a supplied width if that is longer than the VALUE string" do + s = 'Result: Hello.' + @s.rb_sprintf6(8, 5, "Hello").should == s + end + + it "can format a nil VALUE as a pointer and gives the same output as sprintf in C" do + res = @s.rb_sprintf7("%p", nil); + res[0].should == res[1] + end + + it "can format a string VALUE as a pointer and gives the same output as sprintf in C" do + res = @s.rb_sprintf7("%p", "Hello") + res[0].should == res[1] + end + + it "can format a raw number a pointer and gives the same output as sprintf in C" do + res = @s.rb_sprintf7("%p", 0x223643); + res[0].should == res[1] + end end describe "rb_vsprintf" do @@ -1046,11 +1181,11 @@ end end it "raises a TypeError if #to_str does not return a string" do - -> { @s.rb_String(CApiStringSpecs::InvalidTostrTest.new) }.should raise_error(TypeError) + -> { @s.rb_String(CApiStringSpecs::InvalidTostrTest.new) }.should.raise(TypeError) end it "tries to convert the passed argument to a string by calling #to_s" do - @s.rb_String({"bar" => "foo"}).should == '{"bar"=>"foo"}' + @s.rb_String({"bar" => "foo"}).should == {"bar" => "foo"}.to_s end end @@ -1064,11 +1199,11 @@ end end it "raises an error if a string contains a null" do - -> { @s.rb_string_value_cstr("Hello\0 with a null.") }.should raise_error(ArgumentError) + -> { @s.rb_string_value_cstr("Hello\0 with a null.") }.should.raise(ArgumentError) end it "raises an error if a UTF-16 string contains a null" do - -> { @s.rb_string_value_cstr("Hello\0 with a null.".encode('UTF-16BE')) }.should raise_error(ArgumentError) + -> { @s.rb_string_value_cstr("Hello\0 with a null.".encode('UTF-16BE')) }.should.raise(ArgumentError) end end @@ -1095,9 +1230,11 @@ end describe "rb_utf8_str_new_static" do it "returns a UTF-8 string of the correct characters and length" do - str = @s.rb_utf8_str_new_static + str, ptr = @s.rb_utf8_str_new_static str.should == "nokogiri" str.encoding.should == Encoding::UTF_8 + + @s.RSTRING_PTR(str).should == ptr end end @@ -1116,4 +1253,306 @@ end str.encoding.should == Encoding::UTF_8 end end + + describe "rb_str_vcatf" do + it "appends the message to the string" do + @s.rb_str_vcatf("").should == "fmt 42 7 number" + + str = "test " + @s.rb_str_vcatf(str) + str.should == "test fmt 42 7 number" + end + end + + describe "rb_str_catf" do + it "appends the message to the string" do + @s.rb_str_catf("").should == "fmt 41 6 number" + + str = "test " + @s.rb_str_catf(str) + str.should == "test fmt 41 6 number" + end + end + + describe "rb_str_locktmp" do + it "raises an error when trying to lock an already locked string" do + str = +"test" + @s.rb_str_locktmp(str).should == str + -> { @s.rb_str_locktmp(str) }.should.raise(RuntimeError, 'temporal locking already locked string') + end + + it "locks a string so that modifications would raise an error" do + str = +"test" + @s.rb_str_locktmp(str).should == str + -> { str.upcase! }.should.raise(RuntimeError, 'can\'t modify string; temporarily locked') + end + + ruby_version_is "4.0" do + it "raises FrozenError if string is frozen" do + str = -"rb_str_locktmp" + -> { @s.rb_str_locktmp(str) }.should.raise(FrozenError) + + str = +"rb_str_locktmp" + str.freeze + -> { @s.rb_str_locktmp(str) }.should.raise(FrozenError) + end + end + end + + describe "rb_str_unlocktmp" do + it "unlocks a locked string" do + str = +"test" + @s.rb_str_locktmp(str) + @s.rb_str_unlocktmp(str).should == str + str.upcase!.should == "TEST" + end + + it "raises an error when trying to unlock an already unlocked string" do + -> { @s.rb_str_unlocktmp(+"test") }.should.raise(RuntimeError, 'temporal unlocking already unlocked string') + end + + ruby_version_is "4.0" do + it "raises FrozenError if string is frozen" do + str = -"rb_str_locktmp" + -> { @s.rb_str_unlocktmp(str) }.should.raise(FrozenError) + + str = +"rb_str_locktmp" + str.freeze + -> { @s.rb_str_unlocktmp(str) }.should.raise(FrozenError) + end + end + end + + describe "rb_enc_interned_str_cstr" do + it "returns a frozen string" do + str = "hello" + val = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII) + + val.should.is_a?(String) + val.encoding.should == Encoding::US_ASCII + val.should.frozen? + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII) + result2 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII) + result1.should.equal?(result2) + end + + it "returns different frozen strings for different encodings" do + str = "hello" + result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII) + result2 = @s.rb_enc_interned_str_cstr(str, Encoding::UTF_8) + result1.should_not.equal?(result2) + end + + it "returns the same string as String#-@" do + @s.rb_enc_interned_str_cstr("hello", Encoding::UTF_8).should.equal?(-"hello") + end + + ruby_bug "#20322", ""..."3.4" do + it "uses the default encoding if encoding is null" do + str = "hello" + val = @s.rb_enc_interned_str_cstr(str, nil) + val.encoding.should == Encoding::ASCII_8BIT + end + end + end + + describe "rb_enc_interned_str" do + it "returns a frozen string" do + str = "hello" + val = @s.rb_enc_interned_str(str, str.bytesize, Encoding::US_ASCII) + + val.should.is_a?(String) + val.encoding.should == Encoding::US_ASCII + val.should.frozen? + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::US_ASCII) + result2 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::US_ASCII) + result1.should.equal?(result2) + end + + it "returns different frozen strings for different encodings" do + str = "hello" + result1 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::US_ASCII) + result2 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8) + result1.should_not.equal?(result2) + end + + it 'returns the same string when using non-ascii characters' do + str = 'こんにちは' + result1 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8) + result2 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8) + result1.should.equal?(result2) + end + + it "returns the same string as String#-@" do + str = "hello" + @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8).should.equal?(-str) + end + + ruby_bug "#20322", ""..."3.4" do + it "uses the default encoding if encoding is null" do + str = "hello" + val = @s.rb_enc_interned_str(str, str.bytesize, nil) + val.encoding.should == Encoding::ASCII_8BIT + end + end + end + + describe "rb_str_to_interned_str" do + it "returns a frozen string" do + str = "hello" + result = @s.rb_str_to_interned_str(str) + result.should.is_a?(String) + result.should.frozen? + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_str_to_interned_str(str) + result2 = @s.rb_str_to_interned_str(str) + result1.should.equal?(result2) + end + + it "returns different frozen strings for different encodings" do + result1 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::US_ASCII)) + result2 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::UTF_8)) + result1.should_not.equal?(result2) + end + + it "preserves the encoding of the original string" do + result1 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::US_ASCII)) + result2 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::UTF_8)) + result1.encoding.should == Encoding::US_ASCII + result2.encoding.should == Encoding::UTF_8 + end + + it "returns the same string as String#-@" do + @s.rb_str_to_interned_str("hello").should.equal?(-"hello") + end + end + + describe "rb_interned_str" do + it "returns a frozen string" do + str = "hello" + result = @s.rb_interned_str(str, str.bytesize) + result.should.is_a?(String) + result.should.frozen? + result.encoding.should == Encoding::US_ASCII + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_interned_str(str, str.bytesize) + result2 = @s.rb_interned_str(str, str.bytesize) + result1.should.equal?(result2) + end + + it "supports strings with embedded null bytes" do + str = "foo\x00bar\x00baz".b + result = @s.rb_interned_str(str, str.bytesize) + result.should == str + end + + it "return US_ASCII encoding for an empty string" do + result = @s.rb_interned_str("", 0) + result.should == "" + result.encoding.should == Encoding::US_ASCII + end + + it "returns US_ASCII encoding for strings of only 7 bit ASCII" do + 0x00.upto(0x7f).each do |char| + result = @s.rb_interned_str(char.chr, 1) + result.encoding.should == Encoding::US_ASCII + end + end + + ruby_bug "21842", ""..."4.1" do + it "returns BINARY encoding for strings that use the 8th bit" do + 0x80.upto(0xff) do |char| + result = @s.rb_interned_str(char.chr, 1) + result.encoding.should == Encoding::BINARY + end + end + end + + it 'returns the same string when using non-ascii characters' do + str = 'こんにちは' + result1 = @s.rb_interned_str(str, str.bytesize) + result2 = @s.rb_interned_str(str, str.bytesize) + result1.should.equal?(result2) + end + + ruby_bug "21842", ""..."4.1" do + it "returns the same string as String#-@" do + str = "hello".dup.force_encoding(Encoding::US_ASCII) + @s.rb_interned_str(str, str.bytesize).should.equal?(-str) + end + end + end + + describe "rb_interned_str_cstr" do + it "returns a frozen string" do + str = "hello" + result = @s.rb_interned_str_cstr(str) + result.should.is_a?(String) + result.should.frozen? + result.encoding.should == Encoding::US_ASCII + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_interned_str_cstr(str) + result2 = @s.rb_interned_str_cstr(str) + result1.should.equal?(result2) + end + + it "does not support strings with embedded null bytes" do + str = "foo\x00bar\x00baz".b + result = @s.rb_interned_str_cstr(str) + result.should == "foo" + end + + it "return US_ASCII encoding for an empty string" do + result = @s.rb_interned_str_cstr("") + result.should == "" + result.encoding.should == Encoding::US_ASCII + end + + it "returns US_ASCII encoding for strings of only 7 bit ASCII" do + 0x01.upto(0x7f).each do |char| + result = @s.rb_interned_str_cstr(char.chr) + result.encoding.should == Encoding::US_ASCII + end + end + + ruby_bug "21842", ""..."4.1" do + it "returns BINARY encoding for strings that use the 8th bit" do + 0x80.upto(0xff) do |char| + result = @s.rb_interned_str_cstr(char.chr) + result.encoding.should == Encoding::BINARY + end + end + end + + it 'returns the same string when using non-ascii characters' do + str = 'こんにちは' + result1 = @s.rb_interned_str_cstr(str) + result2 = @s.rb_interned_str_cstr(str) + result1.should.equal?(result2) + end + + ruby_bug "21842", ""..."4.1" do + it "returns the same string as String#-@" do + str = "hello".dup.force_encoding(Encoding::US_ASCII) + @s.rb_interned_str_cstr(str).should.equal?(-str) + end + end + end end diff --git a/spec/ruby/optional/capi/struct_spec.rb b/spec/ruby/optional/capi/struct_spec.rb index 0e9e366908..843387bc19 100644 --- a/spec/ruby/optional/capi/struct_spec.rb +++ b/spec/ruby/optional/capi/struct_spec.rb @@ -25,11 +25,11 @@ describe "C-API Struct function" do it "has a value of nil for the member of a newly created instance" do # Verify that attributes are on an instance basis - Struct::CAPIStruct.new.b.should be_nil + Struct::CAPIStruct.new.b.should == nil end it "creates a constant scoped under Struct for the named Struct" do - Struct.should have_constant(:CAPIStruct) + Struct.should.const_defined?(:CAPIStruct, false) end it "returns the member names as Symbols" do @@ -80,15 +80,15 @@ describe "C-API Struct function" do it "has a value of nil for the member of a newly created instance" do # Verify that attributes are on an instance basis - CApiStructSpecs::CAPIStructUnder.new.b.should be_nil + CApiStructSpecs::CAPIStructUnder.new.b.should == nil end it "does not create a constant scoped under Struct for the named Struct" do - Struct.should_not have_constant(:CAPIStructUnder) + Struct.should_not.const_defined?(:CAPIStructUnder) end it "creates a constant scoped under the namespace of the given class" do - CApiStructSpecs.should have_constant(:CAPIStructUnder) + CApiStructSpecs.should.const_defined?(:CAPIStructUnder, false) end it "returns the member names as Symbols" do @@ -106,11 +106,11 @@ describe "C-API Struct function" do describe "rb_struct_define" do it "raises an ArgumentError if arguments contain duplicate member name" do - -> { @s.rb_struct_define(nil, "a", "b", "a") }.should raise_error(ArgumentError) + -> { @s.rb_struct_define(nil, "a", "b", "a") }.should.raise(ArgumentError) end it "raises a NameError if an invalid constant name is given" do - -> { @s.rb_struct_define("foo", "a", "b", "c") }.should raise_error(NameError) + -> { @s.rb_struct_define("foo", "a", "b", "c") }.should.raise(NameError) end end @@ -131,12 +131,12 @@ describe "C-API Struct function" do end it "raises a NameError if the struct member does not exist" do - -> { @s.rb_struct_aref(@struct, :d) }.should raise_error(NameError) + -> { @s.rb_struct_aref(@struct, :d) }.should.raise(NameError) end it "raises an IndexError if the given index is out of range" do - -> { @s.rb_struct_aref(@struct, -4) }.should raise_error(IndexError) - -> { @s.rb_struct_aref(@struct, 3) }.should raise_error(IndexError) + -> { @s.rb_struct_aref(@struct, -4) }.should.raise(IndexError) + -> { @s.rb_struct_aref(@struct, 3) }.should.raise(IndexError) end end @@ -147,7 +147,7 @@ describe "C-API Struct function" do end it "raises a NameError if the struct member does not exist" do - -> { @s.rb_struct_getmember(@struct, :d) }.should raise_error(NameError) + -> { @s.rb_struct_getmember(@struct, :d) }.should.raise(NameError) end end @@ -180,17 +180,17 @@ describe "C-API Struct function" do end it "raises a NameError if the struct member does not exist" do - -> { @s.rb_struct_aset(@struct, :d, 1) }.should raise_error(NameError) + -> { @s.rb_struct_aset(@struct, :d, 1) }.should.raise(NameError) end it "raises an IndexError if the given index is out of range" do - -> { @s.rb_struct_aset(@struct, -4, 1) }.should raise_error(IndexError) - -> { @s.rb_struct_aset(@struct, 3, 1) }.should raise_error(IndexError) + -> { @s.rb_struct_aset(@struct, -4, 1) }.should.raise(IndexError) + -> { @s.rb_struct_aset(@struct, 3, 1) }.should.raise(IndexError) end it "raises a FrozenError if the struct is frozen" do @struct.freeze - -> { @s.rb_struct_aset(@struct, :a, 1) }.should raise_error(FrozenError) + -> { @s.rb_struct_aset(@struct, :a, 1) }.should.raise(FrozenError) end end @@ -208,4 +208,107 @@ describe "C-API Struct function" do @s.rb_struct_size(@struct).should == 3 end end + + describe "rb_struct_initialize" do + it "sets all members" do + @s.rb_struct_initialize(@struct, [1, 2, 3]).should == nil + @struct.a.should == 1 + @struct.b.should == 2 + @struct.c.should == 3 + end + + it "does not freeze the Struct instance" do + @s.rb_struct_initialize(@struct, [1, 2, 3]).should == nil + @struct.should_not.frozen? + @s.rb_struct_initialize(@struct, [4, 5, 6]).should == nil + @struct.a.should == 4 + @struct.b.should == 5 + @struct.c.should == 6 + end + + it "raises ArgumentError if too many values" do + -> { @s.rb_struct_initialize(@struct, [1, 2, 3, 4]) }.should.raise(ArgumentError, "struct size differs") + end + + it "treats missing values as nil" do + @s.rb_struct_initialize(@struct, [1, 2]).should == nil + @struct.a.should == 1 + @struct.b.should == 2 + @struct.c.should == nil + end + end +end + +describe "C-API Data function" do + before :all do + @s = CApiStructSpecs.new + @klass = @s.rb_data_define(nil, "a", "b", "c") + end + + describe "rb_data_define" do + it "returns a subclass of Data class when passed nil as the first argument" do + @klass.should.is_a? Class + @klass.superclass.should == Data + end + + it "returns a subclass of a class when passed as the first argument" do + superclass = Class.new(Data) + klass = @s.rb_data_define(superclass, "a", "b", "c") + + klass.should.is_a? Class + klass.superclass.should == superclass + end + + it "creates readers for the members" do + obj = @klass.new(1, 2, 3) + + obj.a.should == 1 + obj.b.should == 2 + obj.c.should == 3 + end + + it "returns the member names as Symbols" do + obj = @klass.new(0, 0, 0) + + obj.members.should == [:a, :b, :c] + end + + it "raises an ArgumentError if arguments contain duplicate member name" do + -> { @s.rb_data_define(nil, "a", "b", "a") }.should.raise(ArgumentError) + end + + it "raises when first argument is not a class" do + -> { @s.rb_data_define([], "a", "b", "c") }.should.raise(TypeError, "wrong argument type Array (expected Class)") + end + end + + describe "rb_struct_initialize" do + it "sets all members for a Data instance" do + data = @klass.allocate + @s.rb_struct_initialize(data, [1, 2, 3]).should == nil + data.a.should == 1 + data.b.should == 2 + data.c.should == 3 + end + + it "freezes the Data instance" do + data = @klass.allocate + @s.rb_struct_initialize(data, [1, 2, 3]).should == nil + data.should.frozen? + -> { @s.rb_struct_initialize(data, [1, 2, 3]) }.should.raise(FrozenError) + end + + it "raises ArgumentError if too many values" do + data = @klass.allocate + -> { @s.rb_struct_initialize(data, [1, 2, 3, 4]) }.should.raise(ArgumentError, "struct size differs") + end + + it "treats missing values as nil" do + data = @klass.allocate + @s.rb_struct_initialize(data, [1, 2]).should == nil + data.a.should == 1 + data.b.should == 2 + data.c.should == nil + end + end end diff --git a/spec/ruby/optional/capi/symbol_spec.rb b/spec/ruby/optional/capi/symbol_spec.rb index b8fda34c0e..12c93c9f27 100644 --- a/spec/ruby/optional/capi/symbol_spec.rb +++ b/spec/ruby/optional/capi/symbol_spec.rb @@ -61,6 +61,10 @@ describe "C-API Symbol function" do it "converts a symbol to a C char array" do @s.rb_id2name(:test_symbol).should == "test_symbol" end + + it "returns (char*) NULL for (ID) 0" do + @s.rb_id2name_id_zero.should == nil + end end describe "rb_id2str" do @@ -72,6 +76,10 @@ describe "C-API Symbol function" do str = "test_symbol".encode(Encoding::UTF_16LE) @s.rb_id2str(str.to_sym).encoding.should == Encoding::UTF_16LE end + + it "returns (VALUE) 0 = Qfalse for (ID) 0" do + @s.rb_id2str_id_zero.should == false + end end describe "rb_intern_str" do diff --git a/spec/ruby/optional/capi/thread_spec.rb b/spec/ruby/optional/capi/thread_spec.rb index df454d1ea8..75e0b94fdf 100644 --- a/spec/ruby/optional/capi/thread_spec.rb +++ b/spec/ruby/optional/capi/thread_spec.rb @@ -50,7 +50,7 @@ describe "C-API Thread function" do end it "returns nil if the value has not been set" do - @t.rb_thread_local_aref(Thread.current, :thread_capi_specs_undefined).should be_nil + @t.rb_thread_local_aref(Thread.current, :thread_capi_specs_undefined).should == nil end end @@ -72,7 +72,7 @@ describe "C-API Thread function" do obj = Object.new proc = -> x { ScratchPad.record x } thr = @t.rb_thread_create(proc, obj) - thr.should be_kind_of(Thread) + thr.should.is_a?(Thread) thr.join ScratchPad.recorded.should == obj end @@ -83,32 +83,42 @@ describe "C-API Thread function" do raise "my error" } thr = @t.rb_thread_create(prc, nil) - thr.should be_kind_of(Thread) + thr.should.is_a?(Thread) -> { thr.join - }.should raise_error(RuntimeError, "my error") + }.should.raise(RuntimeError, "my error") end it "sets the thread's group" do thr = @t.rb_thread_create(-> x { }, nil) begin thread_group = thr.group - thread_group.should be_an_instance_of(ThreadGroup) + thread_group.should.instance_of?(ThreadGroup) ensure thr.join end end end + describe "ruby_native_thread_p" do + it "returns non-zero for a ruby thread" do + @t.ruby_native_thread_p.should == true + end + + it "returns zero for a non ruby thread" do + @t.ruby_native_thread_p_new_thread.should == false + end + end + describe "rb_thread_call_without_gvl" do - it "runs a C function with the global lock unlocked" do + it "runs a C function with the global lock unlocked and can be woken by Thread#wakeup" do thr = Thread.new do @t.rb_thread_call_without_gvl end # Wait until it's blocking... - Thread.pass while thr.status and thr.status != "sleep" + Thread.pass until thr.stop? # The thread status is set to sleep by rb_thread_call_without_gvl(), # but the thread might not be in the blocking read(2) yet, so wait a bit. @@ -118,27 +128,67 @@ describe "C-API Thread function" do thr.wakeup # Make sure it stopped and we got a proper value - thr.value.should be_true + thr.value.should == true end - guard -> { platform_is :mingw and ruby_version_is ""..."2.7" } do - it "runs a C function with the global lock unlocked and unlocks IO with the generic RUBY_UBF_IO" do - thr = Thread.new do - @t.rb_thread_call_without_gvl_with_ubf_io + platform_is_not :windows do + it "runs a C function with the global lock unlocked and can be woken by a signal" do + # Ruby signal handlers run on the main thread, so we need to reverse roles here and have a thread interrupt us + thr = Thread.current + thr.should == Thread.main + + going_to_block = false + interrupter = Thread.new do + # Wait until it's blocking... + Thread.pass until going_to_block and thr.stop? + + # The thread status is set to sleep by rb_thread_call_without_gvl(), + # but the thread might not be in the blocking read(2) yet, so wait a bit. + sleep 0.1 + + # Wake it up by sending a signal + done = false + prev_handler = Signal.trap(:HUP) { done = true } + begin + Process.kill :HUP, Process.pid + sleep 0.001 until done + ensure + Signal.trap(:HUP, prev_handler) + end end - # Wait until it's blocking... - Thread.pass while thr.status and thr.status != "sleep" + going_to_block = true + # Make sure it stopped and we got a proper value + @t.rb_thread_call_without_gvl.should == true - # The thread status is set to sleep by rb_thread_call_without_gvl(), - # but the thread might not be in the blocking read(2) yet, so wait a bit. - sleep 0.1 + interrupter.join + end + end - # Wake it up, causing the unblock function to be run. - thr.wakeup + it "runs a C function with the global lock unlocked and unlocks IO with the generic RUBY_UBF_IO" do + thr = Thread.new do + @t.rb_thread_call_without_gvl_with_ubf_io + end - # Make sure it stopped and we got a proper value - thr.value.should be_true + # Wait until it's blocking... + Thread.pass until thr.stop? + + # The thread status is set to sleep by rb_thread_call_without_gvl(), + # but the thread might not be in the blocking read(2) yet, so wait a bit. + sleep 0.1 + + # Wake it up, causing the unblock function to be run. + thr.wakeup + + # Make sure it stopped and we got a proper value + thr.value.should == true + end + end + + ruby_version_is "4.0" do + describe "ruby_thread_has_gvl_p" do + it "returns true if the current thread has the GVL" do + @t.ruby_thread_has_gvl_p.should == true end end end diff --git a/spec/ruby/optional/capi/time_spec.rb b/spec/ruby/optional/capi/time_spec.rb index 579e81fc19..dc0b09376e 100644 --- a/spec/ruby/optional/capi/time_spec.rb +++ b/spec/ruby/optional/capi/time_spec.rb @@ -16,7 +16,7 @@ describe "CApiTimeSpecs" do describe "TIMET2NUM" do it "returns an Integer" do - @s.TIMET2NUM.should be_kind_of(Integer) + @s.TIMET2NUM.should.is_a?(Integer) end end @@ -32,7 +32,7 @@ describe "CApiTimeSpecs" do it "creates a Time in the local zone with only a timestamp" do with_timezone("Europe/Amsterdam") do time = @s.rb_time_num_new(1232141421, nil) - time.should be_an_instance_of(Time) + time.should.instance_of?(Time) time.to_i.should == 1232141421 platform_is_not :windows do time.gmt_offset.should == 3600 @@ -43,7 +43,7 @@ describe "CApiTimeSpecs" do it "creates a Time with the given offset" do with_timezone("Europe/Amsterdam") do time = @s.rb_time_num_new(1232141421, 7200) - time.should be_an_instance_of(Time) + time.should.instance_of?(Time) time.to_i.should == 1232141421 time.gmt_offset.should == 7200 end @@ -52,7 +52,7 @@ describe "CApiTimeSpecs" do it "creates a Time with a Float timestamp" do with_timezone("Europe/Amsterdam") do time = @s.rb_time_num_new(1.5, 7200) - time.should be_an_instance_of(Time) + time.should.instance_of?(Time) time.to_i.should == 1 time.nsec.should == 500000000 time.gmt_offset.should == 7200 @@ -62,7 +62,7 @@ describe "CApiTimeSpecs" do it "creates a Time with a Rational timestamp" do with_timezone("Europe/Amsterdam") do time = @s.rb_time_num_new(Rational(3, 2), 7200) - time.should be_an_instance_of(Time) + time.should.instance_of?(Time) time.to_i.should == 1 time.nsec.should == 500000000 time.gmt_offset.should == 7200 @@ -73,32 +73,32 @@ describe "CApiTimeSpecs" do describe "rb_time_interval" do it "creates a timeval interval for a Fixnum" do sec, usec = @s.rb_time_interval(1232141421) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1232141421 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 0 end it "creates a timeval interval for a Float" do sec, usec = @s.rb_time_interval(1.5) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 500000 end it "creates a timeval interval for a Rational" do sec, usec = @s.rb_time_interval(Rational(3, 2)) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 500000 end it "throws an argument error for a negative value" do - -> { @s.rb_time_interval(-1232141421) }.should raise_error(ArgumentError) - -> { @s.rb_time_interval(Rational(-3, 2)) }.should raise_error(ArgumentError) - -> { @s.rb_time_interval(-1.5) }.should raise_error(ArgumentError) + -> { @s.rb_time_interval(-1232141421) }.should.raise(ArgumentError) + -> { @s.rb_time_interval(Rational(-3, 2)) }.should.raise(ArgumentError) + -> { @s.rb_time_interval(-1.5) }.should.raise(ArgumentError) end end @@ -106,36 +106,36 @@ describe "CApiTimeSpecs" do describe "rb_time_interval" do it "creates a timeval interval for a Fixnum" do sec, usec = @s.rb_time_interval(1232141421) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1232141421 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 0 end it "creates a timeval interval for a Float" do sec, usec = @s.rb_time_interval(1.5) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 500000 end it "creates a timeval interval for a Rational" do sec, usec = @s.rb_time_interval(Rational(3, 2)) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 500000 end it "throws an argument error for a negative value" do - -> { @s.rb_time_interval(-1232141421) }.should raise_error(ArgumentError) - -> { @s.rb_time_interval(Rational(-3, 2)) }.should raise_error(ArgumentError) - -> { @s.rb_time_interval(-1.5) }.should raise_error(ArgumentError) + -> { @s.rb_time_interval(-1232141421) }.should.raise(ArgumentError) + -> { @s.rb_time_interval(Rational(-3, 2)) }.should.raise(ArgumentError) + -> { @s.rb_time_interval(-1.5) }.should.raise(ArgumentError) end it "throws an argument error when given a Time instance" do - -> { @s.rb_time_interval(Time.now) }.should raise_error(TypeError) + -> { @s.rb_time_interval(Time.now) }.should.raise(TypeError) end end @@ -143,49 +143,49 @@ describe "CApiTimeSpecs" do describe "rb_time_timeval" do it "creates a timeval for a Fixnum" do sec, usec = @s.rb_time_timeval(1232141421) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1232141421 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 0 end it "creates a timeval for a Float" do sec, usec = @s.rb_time_timeval(1.5) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 500000 end it "creates a timeval for a Rational" do sec, usec = @s.rb_time_timeval(Rational(3, 2)) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 500000 end it "creates a timeval for a negative Fixnum" do sec, usec = @s.rb_time_timeval(-1232141421) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == -1232141421 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 0 end it "creates a timeval for a negative Float" do sec, usec = @s.rb_time_timeval(-1.5) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == -2 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 500000 end it "creates a timeval for a negative Rational" do sec, usec = @s.rb_time_timeval(Rational(-3, 2)) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == -2 - usec.should be_kind_of(Integer) + usec.should.is_a?(Integer) usec.should == 500000 end @@ -200,49 +200,49 @@ describe "CApiTimeSpecs" do describe "rb_time_timespec" do it "creates a timespec for a Fixnum" do sec, nsec = @s.rb_time_timespec(1232141421) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1232141421 - nsec.should be_kind_of(Integer) + nsec.should.is_a?(Integer) nsec.should == 0 end it "creates a timespec for a Float" do sec, nsec = @s.rb_time_timespec(1.5) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1 - nsec.should be_kind_of(Integer) + nsec.should.is_a?(Integer) nsec.should == 500000000 end it "creates a timespec for a Rational" do sec, nsec = @s.rb_time_timespec(Rational(3, 2)) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == 1 - nsec.should be_kind_of(Integer) + nsec.should.is_a?(Integer) nsec.should == 500000000 end it "creates a timespec for a negative Fixnum" do sec, nsec = @s.rb_time_timespec(-1232141421) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == -1232141421 - nsec.should be_kind_of(Integer) + nsec.should.is_a?(Integer) nsec.should == 0 end it "creates a timespec for a negative Float" do sec, nsec = @s.rb_time_timespec(-1.5) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == -2 - nsec.should be_kind_of(Integer) + nsec.should.is_a?(Integer) nsec.should == 500000000 end it "creates a timespec for a negative Rational" do sec, nsec = @s.rb_time_timespec(Rational(-3, 2)) - sec.should be_kind_of(Integer) + sec.should.is_a?(Integer) sec.should == -2 - nsec.should be_kind_of(Integer) + nsec.should.is_a?(Integer) nsec.should == 500000000 end @@ -261,7 +261,7 @@ describe "CApiTimeSpecs" do describe "when offset given is within range of -86400 and 86400 (exclusive)" do it "sets time's is_gmt to false" do - @s.rb_time_timespec_new(1447087832, 476451125, 0).gmt?.should be_false + @s.rb_time_timespec_new(1447087832, 476451125, 0).gmt?.should == false end it "sets time's offset to the offset given" do @@ -270,7 +270,7 @@ describe "CApiTimeSpecs" do end it "returns time object in UTC if offset given equals INT_MAX - 1" do - @s.rb_time_timespec_new(1447087832, 476451125, 0x7ffffffe).utc?.should be_true + @s.rb_time_timespec_new(1447087832, 476451125, 0x7ffffffe).utc?.should == true end it "returns time object in localtime if offset given equals INT_MAX" do @@ -280,8 +280,13 @@ describe "CApiTimeSpecs" do end it "raises an ArgumentError if offset passed is not within range of -86400 and 86400 (exclusive)" do - -> { @s.rb_time_timespec_new(1447087832, 476451125, 86400) }.should raise_error(ArgumentError) - -> { @s.rb_time_timespec_new(1447087832, 476451125, -86400) }.should raise_error(ArgumentError) + -> { @s.rb_time_timespec_new(1447087832, 476451125, 86400) }.should.raise(ArgumentError) + -> { @s.rb_time_timespec_new(1447087832, 476451125, -86400) }.should.raise(ArgumentError) + end + + it "doesn't call Time.at directly" do + Time.should_not_receive(:at) + @s.rb_time_timespec_new(1447087832, 476451125, 32400).should.is_a?(Time) end end @@ -289,7 +294,7 @@ describe "CApiTimeSpecs" do it "fills a struct timespec with the current time" do now = Time.now time = @s.rb_time_from_timespec(now.utc_offset) - time.should be_an_instance_of(Time) + time.should.instance_of?(Time) (time - now).should be_close(0, TIME_TOLERANCE) end end diff --git a/spec/ruby/optional/capi/tracepoint_spec.rb b/spec/ruby/optional/capi/tracepoint_spec.rb index 2043b7c941..8775715e04 100644 --- a/spec/ruby/optional/capi/tracepoint_spec.rb +++ b/spec/ruby/optional/capi/tracepoint_spec.rb @@ -14,7 +14,7 @@ describe "CApiTracePointSpecs" do describe "rb_tracepoint_new" do it "returns a tracepoint object" do @trace = @s.rb_tracepoint_new(7) - @trace.should be_an_instance_of(TracePoint) + @trace.should.instance_of?(TracePoint) @trace.should_not.enabled? end diff --git a/spec/ruby/optional/capi/typed_data_spec.rb b/spec/ruby/optional/capi/typed_data_spec.rb index 9ccfa562d1..376cfe417f 100644 --- a/spec/ruby/optional/capi/typed_data_spec.rb +++ b/spec/ruby/optional/capi/typed_data_spec.rb @@ -30,7 +30,7 @@ describe "CApiWrappedTypedStruct" do it "throws an exception for a wrong type" do a = @s.typed_wrap_struct(1024) - -> { @s.typed_get_struct_other(a) }.should raise_error(TypeError) + -> { @s.typed_get_struct_other(a) }.should.raise(TypeError) end it "unwraps data for a parent type" do @@ -58,6 +58,17 @@ describe "CApiWrappedTypedStruct" do end end + describe "rb_check_type" do + it "raises an exception when checking typed data objects" do + -> { + a = @s.typed_wrap_struct(1024) + @s.rb_check_type(a, a) + }.should.raise(TypeError) { |e| + e.message.should == 'wrong argument type Object (expected Data)' + } + end + end + describe "rb_check_typeddata" do it "returns data pointer when the struct has the given type" do a = @s.typed_wrap_struct(1024) @@ -71,7 +82,21 @@ describe "CApiWrappedTypedStruct" do it "raises an error for different types" do a = @s.typed_wrap_struct(1024) - -> { @s.rb_check_typeddata_different_type(a) }.should raise_error(TypeError) + -> { @s.rb_check_typeddata_different_type(a) }.should.raise(TypeError) + end + end + + ruby_version_is ""..."4.1" do + describe "RTYPEDDATA_P" do + it "returns true for a typed data" do + a = @s.typed_wrap_struct(1024) + @s.RTYPEDDATA_P(a).should == true + end + + it "returns false for an untyped data object" do + a = @s.untyped_wrap_struct(1024) + @s.RTYPEDDATA_P(a).should == false + end end end end diff --git a/spec/ruby/optional/capi/util_spec.rb b/spec/ruby/optional/capi/util_spec.rb index a90c28a78e..dd3cbb6549 100644 --- a/spec/ruby/optional/capi/util_spec.rb +++ b/spec/ruby/optional/capi/util_spec.rb @@ -11,21 +11,21 @@ describe "C-API Util function" do before :each do @prc = -> { 1 } @acc = [] - @keyword_prefix = 'k' if RUBY_VERSION >= '2.7' ScratchPad.record @acc end it "assigns the required arguments scanned" do - @o.rb_scan_args([1, 2], "2", 2, @acc).should == 2 - ScratchPad.recorded.should == [1, 2] + obj = Object.new + @o.rb_scan_args([obj, 2], "2", 2, @acc).should == 2 + ScratchPad.recorded.should == [obj, 2] end it "raises an ArgumentError if there are insufficient arguments" do - -> { @o.rb_scan_args([1, 2], "3", 0, @acc) }.should raise_error(ArgumentError) + -> { @o.rb_scan_args([1, 2], "3", 0, @acc) }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 3)") end it "raises an ArgumentError if there are too many arguments" do - -> { @o.rb_scan_args([1, 2, 3, 4], "3", 0, @acc) }.should raise_error(ArgumentError) + -> { @o.rb_scan_args([1, 2, 3, 4], "3", 0, @acc) }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 3)") end it "assigns the required and optional arguments scanned" do @@ -48,7 +48,7 @@ describe "C-API Util function" do ScratchPad.recorded.should == [1, 2, [3, 4]] end - it "assigns the required and optional arguments and and empty Array when there are no arguments to splat" do + it "assigns the required and optional arguments and empty Array when there are no arguments to splat" do @o.rb_scan_args([1, 2], "11*", 3, @acc).should == 2 ScratchPad.recorded.should == [1, 2, []] end @@ -100,13 +100,13 @@ describe "C-API Util function" do it "assigns Hash arguments" do h = {a: 1, b: 2} - @o.rb_scan_args([h], "#{@keyword_prefix}0:", 1, @acc).should == 0 + @o.rb_scan_args([h], "k0:", 1, @acc).should == 0 ScratchPad.recorded.should == [h] end it "assigns required and Hash arguments" do h = {a: 1, b: 2} - @o.rb_scan_args([1, h], "#{@keyword_prefix}1:", 2, @acc).should == 1 + @o.rb_scan_args([1, h], "k1:", 2, @acc).should == 1 ScratchPad.recorded.should == [1, h] end @@ -115,22 +115,18 @@ describe "C-API Util function" do ScratchPad.recorded.should == [1, nil] end - ruby_version_is ''...'3.0' do - it "assigns required and Hash arguments with nil Hash" do - suppress_warning do - @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1 - end - ScratchPad.recorded.should == [1, nil] - end + it "rejects the use of nil as a hash" do + -> { + @o.rb_scan_args([1, nil], "1:", 2, @acc) + }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 1)") + ScratchPad.recorded.should == [] end - ruby_version_is '3.0' do - it "rejects the use of nil as a hash" do - -> { - @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1 - }.should raise_error(ArgumentError) - ScratchPad.recorded.should == [] - end + it "rejects the use of of a non-Hash as keywords" do + -> { + @o.rb_scan_args([42], ":", 1, @acc) + }.should.raise(ArgumentError, "wrong number of arguments (given 1, expected 0)") + ScratchPad.recorded.should == [] end it "assigns required and optional arguments with no hash argument given" do @@ -138,61 +134,41 @@ describe "C-API Util function" do ScratchPad.recorded.should == [1, 7, 4] end - it "assigns required, optional, splat, post-splat, Hash and block arguments" do - h = {a: 1, b: 2} - @o.rb_scan_args([1, 2, 3, 4, 5, h], "#{@keyword_prefix}11*1:&", 6, @acc, &@prc).should == 5 - ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc] + it "assigns optional arguments with no hash argument given" do + @o.rb_scan_args([1, 7], "02:", 3, @acc).should == 2 + ScratchPad.recorded.should == [1, 7, nil] end - ruby_version_is ''...'3.0' do - # r43934 - it "rejects non-keyword arguments" do - h = {1 => 2, 3 => 4} - -> { - suppress_warning do - @o.rb_scan_args([h], "#{@keyword_prefix}0:", 1, @acc) - end - }.should raise_error(ArgumentError) - ScratchPad.recorded.should == [] - end + it "assigns optional arguments with no hash argument given and rejects the use of optional nil argument as a hash" do + -> { + @o.rb_scan_args([1, nil], "02:", 3, @acc).should == 2 + }.should_not complain - it "rejects required and non-keyword arguments" do - h = {1 => 2, 3 => 4} - -> { - suppress_warning do - @o.rb_scan_args([1, h], "#{@keyword_prefix}1:", 2, @acc) - end - }.should raise_error(ArgumentError) - ScratchPad.recorded.should == [] - end + ScratchPad.recorded.should == [1, nil, nil] + end - it "considers the hash as a post argument when there is a splat" do - h = {1 => 2, 3 => 4} - suppress_warning do - @o.rb_scan_args([1, 2, 3, 4, 5, h], "#{@keyword_prefix}11*1:&", 6, @acc, &@prc).should == 6 - end - ScratchPad.recorded.should == [1, 2, [3, 4, 5], h, nil, @prc] - end + it "assigns required, optional, splat, post-splat, Hash and block arguments" do + h = {a: 1, b: 2} + @o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 5 + ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc] end - ruby_version_is '3.0' do - it "does not reject non-symbol keys in keyword arguments" do - h = {1 => 2, 3 => 4} - @o.rb_scan_args([h], "#{@keyword_prefix}0:", 1, @acc).should == 0 - ScratchPad.recorded.should == [h] - end + it "does not reject non-symbol keys in keyword arguments" do + h = {1 => 2, 3 => 4} + @o.rb_scan_args([h], "k0:", 1, @acc).should == 0 + ScratchPad.recorded.should == [h] + end - it "does not reject non-symbol keys in keyword arguments with required argument" do - h = {1 => 2, 3 => 4} - @o.rb_scan_args([1, h], "#{@keyword_prefix}1:", 2, @acc).should == 1 - ScratchPad.recorded.should == [1, h] - end + it "does not reject non-symbol keys in keyword arguments with required argument" do + h = {1 => 2, 3 => 4} + @o.rb_scan_args([1, h], "k1:", 2, @acc).should == 1 + ScratchPad.recorded.should == [1, h] + end - it "considers keyword arguments with non-symbol keys as keywords when using splat and post arguments" do - h = {1 => 2, 3 => 4} - @o.rb_scan_args([1, 2, 3, 4, 5, h], "#{@keyword_prefix}11*1:&", 6, @acc, &@prc).should == 5 - ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc] - end + it "considers keyword arguments with non-symbol keys as keywords when using splat and post arguments" do + h = {1 => 2, 3 => 4} + @o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 5 + ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc] end end @@ -217,7 +193,7 @@ describe "C-API Util function" do it "raises an error if a required argument is not in the hash" do h = { :a => 7, :c => 12, :b => 5 } - -> { @o.rb_get_kwargs(h, [:b, :d], 2, 0) }.should raise_error(ArgumentError, /missing keyword: :?d/) + -> { @o.rb_get_kwargs(h, [:b, :d], 2, 0) }.should.raise(ArgumentError, "missing keyword: :d") h.should == {:a => 7, :c => 12} end @@ -229,7 +205,7 @@ describe "C-API Util function" do it "raises an error if there are additional arguments and optional is positive" do h = { :a => 7, :c => 12, :b => 5 } - -> { @o.rb_get_kwargs(h, [:b, :a], 2, 0) }.should raise_error(ArgumentError, /unknown keyword: :?c/) + -> { @o.rb_get_kwargs(h, [:b, :a], 2, 0) }.should.raise(ArgumentError, "unknown keyword: :c") h.should == {:c => 12} end @@ -240,10 +216,10 @@ describe "C-API Util function" do end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "rb_long2int" do it "raises a RangeError if the value is outside the range of a C int" do - -> { @o.rb_long2int(0xffff_ffff_ffff) }.should raise_error(RangeError) + -> { @o.rb_long2int(0xffff_ffff_ffff) }.should.raise(RangeError) end end @@ -289,7 +265,7 @@ describe "C-API Util function" do describe "rb_sourceline" do it "returns the current ruby file" do - @o.rb_sourceline.should be_kind_of(Integer) + @o.rb_sourceline.should.is_a?(Integer) end end |
