1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
This commit is contained in:
Benoit Daloze 2020-02-28 19:07:17 +01:00
parent 5d21050182
commit a0f5ff4c3c
75 changed files with 851 additions and 143 deletions

View file

@ -53,6 +53,8 @@ which indicates the file was generated but the method unspecified.
Here is a list of frequently-used matchers, which should be enough for most specs. Here is a list of frequently-used matchers, which should be enough for most specs.
There are a few extra specific matchers used in the couple specs that need it. There are a few extra specific matchers used in the couple specs that need it.
#### Comparison matchers
```ruby ```ruby
(1 + 2).should == 3 # Calls #== (1 + 2).should == 3 # Calls #==
(1 + 2).should_not == 5 (1 + 2).should_not == 5
@ -66,7 +68,11 @@ File.should equal(File) # Calls #equal? (tests identity)
4.should > 3 4.should > 3
"Hello".should =~ /l{2}/ # Calls #=~ (Regexp match) "Hello".should =~ /l{2}/ # Calls #=~ (Regexp match)
```
#### Predicate matchers
```ruby
[].should be_empty # Calls #empty? [].should be_empty # Calls #empty?
[1,2,3].should include(2) # Calls #include? [1,2,3].should include(2) # Calls #include?
@ -82,8 +88,13 @@ Numeric.should be_ancestor_of(Float) # Float.ancestors.include?(Numeric)
3.14.should respond_to(:to_i) # Calls #respond_to? 3.14.should respond_to(:to_i) # Calls #respond_to?
Fixnum.should have_instance_method(:+) Fixnum.should have_instance_method(:+)
Array.should have_method(:new) Array.should have_method(:new)
# Also have_constant, have_private_instance_method, have_singleton_method, etc ```
Also `have_constant`, `have_private_instance_method`, `have_singleton_method`, etc.
#### Exception matchers
```ruby
-> { -> {
raise "oops" raise "oops"
}.should raise_error(RuntimeError, /oops/) }.should raise_error(RuntimeError, /oops/)
@ -95,11 +106,20 @@ Array.should have_method(:new)
e.message.should include("oops") e.message.should include("oops")
e.cause.should == nil e.cause.should == nil
} }
```
# To avoid! Instead, use an expectation testing what the code in the lambda does. ##### should_not raise_error
# If an exception is raised, it will fail the example anyway.
**To avoid!** Instead, use an expectation testing what the code in the lambda does.
If an exception is raised, it will fail the example anyway.
```ruby
-> { ... }.should_not raise_error -> { ... }.should_not raise_error
```
#### Warning matcher
```ruby
-> { -> {
Fixnum Fixnum
}.should complain(/constant ::Fixnum is deprecated/) # Expect a warning }.should complain(/constant ::Fixnum is deprecated/) # Expect a warning
@ -110,6 +130,8 @@ Array.should have_method(:new)
Different guards are available as defined by mspec. Different guards are available as defined by mspec.
Here is a list of the most commonly-used guards: Here is a list of the most commonly-used guards:
#### Version guards
```ruby ```ruby
ruby_version_is ""..."2.4" do ruby_version_is ""..."2.4" do
# Specs for RUBY_VERSION < 2.4 # Specs for RUBY_VERSION < 2.4
@ -118,7 +140,11 @@ end
ruby_version_is "2.4" do ruby_version_is "2.4" do
# Specs for RUBY_VERSION >= 2.4 # Specs for RUBY_VERSION >= 2.4
end end
```
#### Platform guards
```ruby
platform_is :windows do platform_is :windows do
# Specs only valid on Windows # Specs only valid on Windows
end end
@ -140,18 +166,25 @@ end
big_endian do big_endian do
# Big-endian platform # Big-endian platform
end end
```
# In case there is a bug in MRI but the expected behavior is obvious #### Guard for bug
# First file a bug at https://bugs.ruby-lang.org/
# It is better to use a ruby_version_is guard if there was a release with the fix In case there is a bug in MRI but the expected behavior is obvious.
First, file a bug at https://bugs.ruby-lang.org/.
It is better to use a `ruby_version_is` guard if there was a release with the fix.
```ruby
ruby_bug '#13669', ''...'2.5' do ruby_bug '#13669', ''...'2.5' do
it "works like this" do it "works like this" do
# Specify the expected behavior here, not the bug # Specify the expected behavior here, not the bug
end end
end end
```
#### Combining guards
# Combining guards ```ruby
guard -> { platform_is :windows and ruby_version_is ""..."2.5" } do guard -> { platform_is :windows and ruby_version_is ""..."2.5" } do
# Windows and RUBY_VERSION < 2.5 # Windows and RUBY_VERSION < 2.5
end end
@ -159,8 +192,11 @@ end
guard_not -> { platform_is :windows and ruby_version_is ""..."2.5" } do guard_not -> { platform_is :windows and ruby_version_is ""..."2.5" } do
# The opposite # The opposite
end end
```
# Custom guard #### Custom guard
```ruby
max_uint = (1 << 32) - 1 max_uint = (1 << 32) - 1
guard -> { max_uint <= fixnum_max } do guard -> { max_uint <= fixnum_max } do
end end
@ -168,6 +204,8 @@ end
Custom guards are better than a simple `if` as they allow [mspec commands](https://github.com/ruby/mspec/issues/30#issuecomment-312487779) to work properly. Custom guards are better than a simple `if` as they allow [mspec commands](https://github.com/ruby/mspec/issues/30#issuecomment-312487779) to work properly.
#### Implementation-specific behaviors
In general, the usage of guards should be minimized as possible. In general, the usage of guards should be minimized as possible.
There are no guards to define implementation-specific behavior because There are no guards to define implementation-specific behavior because

View file

@ -1,7 +1,6 @@
# The Ruby Spec Suite # The Ruby Spec Suite
[![Actions Build Status](https://github.com/ruby/spec/workflows/CI/badge.svg?branch=master)](https://github.com/ruby/spec/actions) [![Actions Build Status](https://github.com/ruby/spec/workflows/CI/badge.svg)](https://github.com/ruby/spec/actions)
[![Windows Actions Build Status](https://github.com/ruby/spec/workflows/Windows/badge.svg?branch=master)](https://github.com/ruby/spec/actions)
[![Gitter](https://badges.gitter.im/ruby/spec.svg)](https://gitter.im/ruby/spec) [![Gitter](https://badges.gitter.im/ruby/spec.svg)](https://gitter.im/ruby/spec)
The Ruby Spec Suite, abbreviated `ruby/spec`, is a test suite for the behavior of the Ruby programming language. The Ruby Spec Suite, abbreviated `ruby/spec`, is a test suite for the behavior of the Ruby programming language.

View file

@ -0,0 +1,33 @@
require_relative '../spec_helper'
describe "The -l command line option" do
before :each do
@names = fixture __FILE__, "full_names.txt"
end
it "chomps lines with default separator" do
ruby_exe('puts $_.end_with?("\n")', options: "-n -l", escape: true,
args: " < #{@names}").should ==
"false\nfalse\nfalse\n"
end
ruby_version_is "2.5" do
it "chomps last line based on $/" do
ruby_exe('BEGIN { $/ = "ones\n" }; puts $_', options: "-W0 -n -l", escape: true,
args: " < #{@names}").should ==
"alice j\nbob field\njames grey\n"
end
end
it "sets $\\ to the value of $/" do
ruby_exe("puts $\\ == $/", options: "-W0 -n -l", escape: true,
args: " < #{@names}").should ==
"true\ntrue\ntrue\n"
end
it "sets $-l" do
ruby_exe("puts $-l", options: "-n -l", escape: true,
args: " < #{@names}").should ==
"true\ntrue\ntrue\n"
end
end

View file

@ -461,7 +461,7 @@ describe :array_slice, shared: true do
it "raises a RangeError when the start index is out of range of Fixnum" do it "raises a RangeError when the start index is out of range of Fixnum" do
array = [1, 2, 3, 4, 5, 6] array = [1, 2, 3, 4, 5, 6]
obj = mock('large value') obj = mock('large value')
obj.should_receive(:to_int).and_return(0x8000_0000_0000_0000_0000) obj.should_receive(:to_int).and_return(bignum_value)
-> { array.send(@method, obj) }.should raise_error(RangeError) -> { array.send(@method, obj) }.should raise_error(RangeError)
obj = 8e19 obj = 8e19
@ -471,10 +471,19 @@ describe :array_slice, shared: true do
it "raises a RangeError when the length is out of range of Fixnum" do it "raises a RangeError when the length is out of range of Fixnum" do
array = [1, 2, 3, 4, 5, 6] array = [1, 2, 3, 4, 5, 6]
obj = mock('large value') obj = mock('large value')
obj.should_receive(:to_int).and_return(0x8000_0000_0000_0000_0000) obj.should_receive(:to_int).and_return(bignum_value)
-> { array.send(@method, 1, obj) }.should raise_error(RangeError) -> { array.send(@method, 1, obj) }.should raise_error(RangeError)
obj = 8e19 obj = 8e19
-> { array.send(@method, 1, obj) }.should raise_error(RangeError) -> { array.send(@method, 1, obj) }.should raise_error(RangeError)
end end
it "raises a type error if a range is passed with a length" do
->{ [1, 2, 3].send(@method, 1..2, 1) }.should raise_error(TypeError)
end
it "raises a RangeError if passed a range with a bound that is too large" do
-> { "hello".send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError)
-> { "hello".send(@method, 0..bignum_value) }.should raise_error(RangeError)
end
end end

View file

@ -131,4 +131,10 @@ describe "Binding#eval" do
bind, meth = obj.get_binding_with_send_and_method bind, meth = obj.get_binding_with_send_and_method
bind.eval("__method__").should == meth bind.eval("__method__").should == meth
end end
it "reflects refinements activated in the binding scope" do
bind = BindingSpecs::Refined.refined_binding
bind.eval("'bar'.foo").should == "foo"
end
end end

View file

@ -49,4 +49,18 @@ module BindingSpecs
end end
end end
end end
module AddFooToString
refine(String) do
def foo
"foo"
end
end
end
class Refined
using AddFooToString
def self.refined_binding
binding
end
end
end end

View file

@ -67,6 +67,12 @@ platform_is_not :windows do
it "raises Errno::ENOENT if the symlink points to an absent file" do it "raises Errno::ENOENT if the symlink points to an absent file" do
-> { File.realpath(@fake_link) }.should raise_error(Errno::ENOENT) -> { File.realpath(@fake_link) }.should raise_error(Errno::ENOENT)
end end
it "converts the argument with #to_path" do
path = mock("path")
path.should_receive(:to_path).and_return(__FILE__)
File.realpath(path).should == File.realpath(__FILE__ )
end
end end
end end

View file

@ -41,5 +41,14 @@ platform_is_not :windows do
st.file?.should == true st.file?.should == true
st.symlink?.should == false st.symlink?.should == false
end end
it "returns an error when given missing non-ASCII path" do
missing_path = "/missingfilepath\xE3E4".force_encoding("ASCII-8BIT")
-> {
File.stat(missing_path)
}.should raise_error(Errno::ENOENT) { |e|
e.message.should include(missing_path)
}
end
end end
end end

View file

@ -46,6 +46,11 @@ describe "Integer#<=" do
(@bignum <= (@bignum + 0.5)).should == false (@bignum <= (@bignum + 0.5)).should == false
end end
it "returns true for bignums compare to a bigger float" do
(18446744073709551616 <= 1.8446744073709552e+19).should == true
(@bignum <= (@bignum + 9999.0)).should == true
end
it "raises an ArgumentError when given a non-Integer" do it "raises an ArgumentError when given a non-Integer" do
-> { @bignum <= "4" }.should raise_error(ArgumentError) -> { @bignum <= "4" }.should raise_error(ArgumentError)
-> { @bignum <= mock('str') }.should raise_error(ArgumentError) -> { @bignum <= mock('str') }.should raise_error(ArgumentError)

View file

@ -28,6 +28,14 @@ describe 'Kernel#caller_locations' do
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s) locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
end end
ruby_version_is "2.6" do
it "works with endless ranges" do
locations1 = caller_locations(0)
locations2 = caller_locations(eval("(2..)"))
locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
end
end
it "can be called with a range whose end is negative" do it "can be called with a range whose end is negative" do
locations1 = caller_locations(0) locations1 = caller_locations(0)
locations2 = caller_locations(2..-1) locations2 = caller_locations(2..-1)

View file

@ -43,4 +43,12 @@ describe 'Kernel#caller' do
"#{path}:2:in `block in <main>'\n" "#{path}:2:in `block in <main>'\n"
] ]
end end
ruby_version_is "2.6" do
it "works with endless ranges" do
locations1 = KernelSpecs::CallerTest.locations(0)
locations2 = KernelSpecs::CallerTest.locations(eval("(2..)"))
locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
end
end
end end

View file

@ -373,4 +373,44 @@ CODE
EvalSpecs.send :remove_const, :Vπstring_not_frozen EvalSpecs.send :remove_const, :Vπstring_not_frozen
end end
end end
it "activates refinements from the eval scope" do
refinery = Module.new do
refine EvalSpecs::A do
def foo
"bar"
end
end
end
result = nil
Module.new do
using refinery
result = eval "EvalSpecs::A.new.foo"
end
result.should == "bar"
end
it "activates refinements from the binding" do
refinery = Module.new do
refine EvalSpecs::A do
def foo
"bar"
end
end
end
b = nil
m = Module.new do
using refinery
b = binding
end
result = eval "EvalSpecs::A.new.foo", b
result.should == "bar"
end
end end

View file

@ -18,6 +18,12 @@ describe "Module#name" do
m::N.name.should =~ /\A#<Module:0x[0-9a-f]+>::N\z/ m::N.name.should =~ /\A#<Module:0x[0-9a-f]+>::N\z/
end end
it "returns nil for a singleton class" do
Module.new.singleton_class.name.should be_nil
String.singleton_class.name.should be_nil
Object.new.singleton_class.name.should be_nil
end
it "changes when the module is reachable through a constant path" do it "changes when the module is reachable through a constant path" do
m = Module.new m = Module.new
module m::N; end module m::N; end

View file

@ -112,4 +112,48 @@ describe :module_class_eval, shared: true do
a.attribute.should == "A" a.attribute.should == "A"
b.attribute.should == "B" b.attribute.should == "B"
end end
it "activates refinements from the eval scope" do
refinery = Module.new do
refine ModuleSpecs::NamedClass do
def foo
"bar"
end
end
end
mid = @method
result = nil
Class.new do
using refinery
result = send(mid, "ModuleSpecs::NamedClass.new.foo")
end
result.should == "bar"
end
it "activates refinements from the eval scope with block" do
refinery = Module.new do
refine ModuleSpecs::NamedClass do
def foo
"bar"
end
end
end
mid = @method
result = nil
Class.new do
using refinery
result = send(mid) do
ModuleSpecs::NamedClass.new.foo
end
end
result.should == "bar"
end
end end

View file

@ -15,5 +15,9 @@ ruby_version_is "2.6" do
(o =~ true).should be_nil (o =~ true).should be_nil
end end
end end
it "should not warn" do
-> { nil =~ /a/ }.should_not complain(verbose: true)
end
end end
end end

View file

@ -121,6 +121,8 @@ describe :string_slice_index_length, shared: true do
"x".send(@method, -2,0).should == nil "x".send(@method, -2,0).should == nil
"x".send(@method, -2,1).should == nil "x".send(@method, -2,1).should == nil
"x".send(@method, fixnum_max, 1).should == nil
end end
it "returns nil if the length is negative" do it "returns nil if the length is negative" do
@ -293,6 +295,24 @@ describe :string_slice_range, shared: true do
"hello world".send(@method, 6..5).send(@method, -1..-1).should == nil "hello world".send(@method, 6..5).send(@method, -1..-1).should == nil
"hello world".send(@method, 6..5).send(@method, 1..1).should == nil "hello world".send(@method, 6..5).send(@method, 1..1).should == nil
end end
it "raises a type error if a range is passed with a length" do
->{ "hello".send(@method, 1..2, 1) }.should raise_error(TypeError)
end
it "raises a RangeError if one of the bound is too big" do
-> { "hello".send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError)
-> { "hello".send(@method, 0..bignum_value) }.should raise_error(RangeError)
end
ruby_version_is "2.6" do
it "works with endless ranges" do
"hello there".send(@method, eval("(2..)")).should == "llo there"
"hello there".send(@method, eval("(2...)")).should == "llo there"
"hello there".send(@method, eval("(-4..)")).should == "here"
"hello there".send(@method, eval("(-4...)")).should == "here"
end
end
end end
describe :string_slice_regexp, shared: true do describe :string_slice_regexp, shared: true do

View file

@ -15,7 +15,7 @@ describe "Struct.new" do
second = nil second = nil
-> { -> {
second = Struct.new('Person', :hair, :sex) second = Struct.new('Person', :hair, :sex)
}.should complain(/redefining constant/) }.should complain(/constant/)
second.should == Struct::Person second.should == Struct::Person
first.members.should_not == second.members first.members.should_not == second.members

View file

@ -22,28 +22,33 @@ describe "Thread#backtrace_locations" do
it "can be called with a number of locations to omit" do it "can be called with a number of locations to omit" do
locations1 = Thread.current.backtrace_locations locations1 = Thread.current.backtrace_locations
locations2 = Thread.current.backtrace_locations(2) locations2 = Thread.current.backtrace_locations(2)
locations1[2..-1].length.should == locations2.length locations2.length.should == locations1[2..-1].length
locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s) locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
end end
it "can be called with a maximum number of locations to return as second parameter" do it "can be called with a maximum number of locations to return as second parameter" do
locations1 = Thread.current.backtrace_locations locations1 = Thread.current.backtrace_locations
locations2 = Thread.current.backtrace_locations(2, 3) locations2 = Thread.current.backtrace_locations(2, 3)
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s) locations2.map(&:to_s).should == locations1[2..4].map(&:to_s)
end end
it "can be called with a range" do it "can be called with a range" do
locations1 = Thread.current.backtrace_locations locations1 = Thread.current.backtrace_locations
locations2 = Thread.current.backtrace_locations(2..4) locations2 = Thread.current.backtrace_locations(2..4)
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s) locations2.map(&:to_s).should == locations1[2..4].map(&:to_s)
end end
it "can be called with a range whose end is negative" do it "can be called with a range whose end is negative" do
locations1 = Thread.current.backtrace_locations Thread.current.backtrace_locations(2..-1).map(&:to_s).should == Thread.current.backtrace_locations[2..-1].map(&:to_s)
locations2 = Thread.current.backtrace_locations(2..-1) Thread.current.backtrace_locations(2..-2).map(&:to_s).should == Thread.current.backtrace_locations[2..-2].map(&:to_s)
locations3 = Thread.current.backtrace_locations(2..-2) end
locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
locations1[2..-2].map(&:to_s).should == locations3.map(&:to_s) ruby_version_is "2.6" do
it "can be called with an endless range" do
locations1 = Thread.current.backtrace_locations(0)
locations2 = Thread.current.backtrace_locations(eval("(2..)"))
locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
end
end end
it "returns nil if omitting more locations than available" do it "returns nil if omitting more locations than available" do

View file

@ -32,4 +32,38 @@ describe "Thread#backtrace" do
t.join t.join
backtrace.should be_kind_of(Array) backtrace.should be_kind_of(Array)
end end
it "can be called with a number of locations to omit" do
locations1 = Thread.current.backtrace
locations2 = Thread.current.backtrace(2)
locations1[2..-1].length.should == locations2.length
locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
end
it "can be called with a maximum number of locations to return as second parameter" do
locations1 = Thread.current.backtrace
locations2 = Thread.current.backtrace(2, 3)
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
end
it "can be called with a range" do
locations1 = Thread.current.backtrace
locations2 = Thread.current.backtrace(2..4)
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
end
it "can be called with a range whose end is negative" do
Thread.current.backtrace(2..-1).should == Thread.current.backtrace[2..-1]
Thread.current.backtrace(2..-2).should == Thread.current.backtrace[2..-2]
end
it "returns nil if omitting more locations than available" do
Thread.current.backtrace(100).should == nil
Thread.current.backtrace(100..-1).should == nil
end
it "returns [] if omitting exactly the number of locations available" do
omit = Thread.current.backtrace.length
Thread.current.backtrace(omit).should == []
end
end end

View file

@ -36,10 +36,11 @@ ruby_version_is "2.7" do
it "copies own timezone to the returning value" do it "copies own timezone to the returning value" do
@time.zone.should == @time.ceil.zone @time.zone.should == @time.ceil.zone
with_timezone "JST-9" do time = with_timezone "JST-9" do
time = Time.at 0, 1 Time.at 0, 1
end
time.zone.should == time.ceil.zone time.zone.should == time.ceil.zone
end end
end end
end
end end

View file

@ -28,10 +28,11 @@ ruby_version_is "2.7" do
it "copies own timezone to the returning value" do it "copies own timezone to the returning value" do
@time.zone.should == @time.floor.zone @time.zone.should == @time.floor.zone
with_timezone "JST-9" do time = with_timezone "JST-9" do
time = Time.at 0, 1 Time.at 0, 1
end
time.zone.should == time.floor.zone time.zone.should == time.floor.zone
end end
end end
end
end end

View file

@ -0,0 +1,41 @@
require_relative '../spec_helper'
require_relative 'fixtures/delegation'
ruby_version_is "2.7" do
describe "delegation with def(...)" do
it "delegates rest and kwargs" do
a = Class.new(DelegationSpecs::Target)
a.class_eval(<<-RUBY)
def delegate(...)
target(...)
end
RUBY
a.new.delegate(1, b: 2).should == [[1], {b: 2}]
end
it "delegates block" do
a = Class.new(DelegationSpecs::Target)
a.class_eval(<<-RUBY)
def delegate_block(...)
target_block(...)
end
RUBY
a.new.delegate_block(1, b: 2) { |x| x }.should == [{b: 2}, [1]]
end
it "parses as open endless Range when brackets are ommitted" do
a = Class.new(DelegationSpecs::Target)
suppress_warning do
a.class_eval(<<-RUBY)
def delegate(...)
target ...
end
RUBY
end
a.new.delegate(1, b: 2).should == Range.new([[], {}], nil, true)
end
end
end

View file

@ -0,0 +1,11 @@
module DelegationSpecs
class Target
def target(*args, **kwargs)
[args, kwargs]
end
def target_block(*args, **kwargs)
yield [kwargs, args]
end
end
end

View file

@ -402,6 +402,17 @@ ruby_version_is "2.7" do
RUBY RUBY
}.should raise_error(SyntaxError, /illegal variable in alternative pattern/) }.should raise_error(SyntaxError, /illegal variable in alternative pattern/)
end end
it "support undescore prefixed variables in alternation" do
eval(<<~RUBY).should == true
case [0, 1]
in [1, _]
false
in [0, 0] | [0, _a]
true
end
RUBY
end
end end
describe "AS pattern" do describe "AS pattern" do
@ -962,5 +973,79 @@ ruby_version_is "2.7" do
RUBY RUBY
end end
end end
describe "refinements" do
it "are used for #deconstruct" do
refinery = Module.new do
refine Array do
def deconstruct
[0]
end
end
end
result = nil
Module.new do
using refinery
result = eval(<<~RUBY)
case []
in [0]
true
end
RUBY
end
result.should == true
end
it "are used for #deconstruct_keys" do
refinery = Module.new do
refine Hash do
def deconstruct_keys(_)
{a: 0}
end
end
end
result = nil
Module.new do
using refinery
result = eval(<<~RUBY)
case {}
in a: 0
true
end
RUBY
end
result.should == true
end
it "are used for #=== in constant pattern" do
refinery = Module.new do
refine Array.singleton_class do
def ===(obj)
obj.is_a?(Hash)
end
end
end
result = nil
Module.new do
using refinery
result = eval(<<~RUBY)
case {}
in Array
true
end
RUBY
end
result.should == true
end
end
end end
end end

View file

@ -354,6 +354,16 @@ describe "Multiple assignment" do
a.should be_an_instance_of(Array) a.should be_an_instance_of(Array)
end end
it "unfreezes the array returned from calling 'to_a' on the splatted value" do
obj = Object.new
def obj.to_a
[1,2].freeze
end
res = *obj
res.should == [1,2]
res.frozen?.should == false
end
it "consumes values for an anonymous splat" do it "consumes values for an anonymous splat" do
a = 1 a = 1
(* = *a).should == [1] (* = *a).should == [1]

View file

@ -6,6 +6,10 @@ describe "CGI.escapeHTML" do
CGI.escapeHTML(%[& < > " ']).should == '&amp; &lt; &gt; &quot; &#39;' CGI.escapeHTML(%[& < > " ']).should == '&amp; &lt; &gt; &quot; &#39;'
end end
it "escapes invalid encoding" do
CGI.escapeHTML(%[<\xA4??>]).should == "&lt;\xA4??&gt;"
end
it "does not escape any other characters" do it "does not escape any other characters" do
chars = " !\#$%()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" chars = " !\#$%()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
CGI.escapeHTML(chars).should == chars CGI.escapeHTML(chars).should == chars

View file

@ -6,4 +6,18 @@ describe "StringIO#binmode" do
io = StringIO.new("example") io = StringIO.new("example")
io.binmode.should equal(io) io.binmode.should equal(io)
end end
it "changes external encoding to BINARY" do
io = StringIO.new
io.external_encoding.should == Encoding.find('locale')
io.binmode
io.external_encoding.should == Encoding::BINARY
end
it "does not set internal encoding" do
io = StringIO.new
io.internal_encoding.should == nil
io.binmode
io.internal_encoding.should == nil
end
end end

View file

@ -0,0 +1,19 @@
require_relative '../../spec_helper'
require "stringio"
describe "StringIO#inspect" do
it "returns the same as #to_s" do
io = StringIO.new("example")
io.inspect.should == io.to_s
end
it "does not include the contents" do
io = StringIO.new("contents")
io.inspect.should_not include("contents")
end
it "uses the regular Object#inspect without any instance variable" do
io = StringIO.new("example")
io.inspect.should =~ /\A#<StringIO:0x\h+>\z/
end
end

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -1,2 +0,0 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'

View file

@ -8,9 +8,6 @@ specs:
optional/capi/array_spec.rb optional/capi/array_spec.rb
2. Put the C file containing the C functions for array_spec.rb in 2. Put the C file containing the C functions for array_spec.rb in
optional/capi/ext/array_spec.c optional/capi/ext/array_spec.c
3. Add a '#define HAVE_RB_ARY_NEW 1' to rubyspec.h 3. Name the C extension class 'CApiArraySpecs'.
4. Name the C extension class 'CApiArraySpecs'. 4. Name the C functions 'array_spec_rb_ary_new'.
5. Name the C functions 'array_spec_rb_ary_new'. 5. Attach the C function to the class using the name 'rb_ary_new'
6. Wrap the code in the optional/capi/ext/array_spec.c in
'#ifdef HAVE_RB_ARY_NEW'
7. Attach the C function to the class using the name 'rb_ary_new'

View file

@ -272,6 +272,16 @@ describe "C-API Array function" do
end end
end end
describe "RARRAY_ASET" do
# This macro does NOT do any bounds checking!
it "writes an element in the array" do
ary = [1, 2, 3]
@s.RARRAY_ASET(ary, 0, 0)
@s.RARRAY_ASET(ary, 2, 42)
ary.should == [0, 2, 42]
end
end
describe "rb_assoc_new" do describe "rb_assoc_new" do
it "returns an array containing the two elements" do it "returns an array containing the two elements" do
@s.rb_assoc_new(1, 2).should == [1, 2] @s.rb_assoc_new(1, 2).should == [1, 2]

View file

@ -54,6 +54,11 @@ static VALUE array_spec_RARRAY_AREF(VALUE self, VALUE array, VALUE index) {
return RARRAY_AREF(array, FIX2INT(index)); return RARRAY_AREF(array, FIX2INT(index));
} }
static VALUE array_spec_RARRAY_ASET(VALUE self, VALUE array, VALUE index, VALUE value) {
RARRAY_ASET(array, FIX2INT(index), value);
return value;
}
static VALUE array_spec_rb_ary_aref(int argc, VALUE *argv, VALUE self) { static VALUE array_spec_rb_ary_aref(int argc, VALUE *argv, VALUE self) {
VALUE ary, args; VALUE ary, args;
rb_scan_args(argc, argv, "1*", &ary, &args); rb_scan_args(argc, argv, "1*", &ary, &args);
@ -244,6 +249,7 @@ void Init_array_spec(void) {
rb_define_method(cls, "RARRAY_PTR_assign", array_spec_RARRAY_PTR_assign, 2); rb_define_method(cls, "RARRAY_PTR_assign", array_spec_RARRAY_PTR_assign, 2);
rb_define_method(cls, "RARRAY_PTR_memcpy", array_spec_RARRAY_PTR_memcpy, 2); rb_define_method(cls, "RARRAY_PTR_memcpy", array_spec_RARRAY_PTR_memcpy, 2);
rb_define_method(cls, "RARRAY_AREF", array_spec_RARRAY_AREF, 2); rb_define_method(cls, "RARRAY_AREF", array_spec_RARRAY_AREF, 2);
rb_define_method(cls, "RARRAY_ASET", array_spec_RARRAY_ASET, 3);
rb_define_method(cls, "rb_ary_aref", array_spec_rb_ary_aref, -1); rb_define_method(cls, "rb_ary_aref", array_spec_rb_ary_aref, -1);
rb_define_method(cls, "rb_ary_clear", array_spec_rb_ary_clear, 1); rb_define_method(cls, "rb_ary_clear", array_spec_rb_ary_clear, 1);
rb_define_method(cls, "rb_ary_delete", array_spec_rb_ary_delete, 2); rb_define_method(cls, "rb_ary_delete", array_spec_rb_ary_delete, 2);

View file

@ -65,7 +65,7 @@ static VALUE bignum_spec_rb_big_pack_array(VALUE self, VALUE val, VALUE len) {
long long_len = NUM2LONG(len); long long_len = NUM2LONG(len);
VALUE ary = rb_ary_new_capa(long_len); VALUE ary = rb_ary_new_capa(long_len);
unsigned long *buf = malloc(long_len * SIZEOF_LONG); unsigned long *buf = (unsigned long*) malloc(long_len * SIZEOF_LONG);
/* The array should be filled with recognisable junk so we can check /* The array should be filled with recognisable junk so we can check
it is all cleared properly. */ it is all cleared properly. */
@ -102,5 +102,5 @@ void Init_bignum_spec(void) {
} }
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { }
#endif #endif

View file

@ -29,5 +29,5 @@ void Init_boolean_spec(void) {
} }
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { }
#endif #endif

View file

@ -1,5 +1,13 @@
#include "ruby.h" #include "ruby.h"
#ifdef __cplusplus
extern "C" {
#endif
void Init_class_id_under_autoload_spec(void) { void Init_class_id_under_autoload_spec(void) {
rb_define_class_id_under(rb_cObject, rb_intern("ClassIdUnderAutoload"), rb_cObject); rb_define_class_id_under(rb_cObject, rb_intern("ClassIdUnderAutoload"), rb_cObject);
} }
#ifdef __cplusplus
}
#endif

View file

@ -45,7 +45,7 @@ static VALUE class_spec_rb_class_new_instance(VALUE self,
VALUE nargs, VALUE args, VALUE nargs, VALUE args,
VALUE klass) { VALUE klass) {
int c_nargs = FIX2INT(nargs); int c_nargs = FIX2INT(nargs);
VALUE *c_args = alloca(sizeof(VALUE) * c_nargs); VALUE *c_args = (VALUE*)alloca(sizeof(VALUE) * c_nargs);
int i; int i;
for (i = 0; i < c_nargs; i++) for (i = 0; i < c_nargs; i++)

View file

@ -1,5 +1,13 @@
#include "ruby.h" #include "ruby.h"
#ifdef __cplusplus
extern "C" {
#endif
void Init_class_under_autoload_spec(void) { void Init_class_under_autoload_spec(void) {
rb_define_class_under(rb_cObject, "ClassUnderAutoload", rb_cObject); rb_define_class_under(rb_cObject, "ClassUnderAutoload", rb_cObject);
} }
#ifdef __cplusplus
}
#endif

View file

@ -19,7 +19,7 @@ void sample_wrapped_struct_mark(void* st) {
} }
VALUE sdaf_alloc_func(VALUE klass) { VALUE sdaf_alloc_func(VALUE klass) {
struct sample_wrapped_struct* bar = malloc(sizeof(struct sample_wrapped_struct)); struct sample_wrapped_struct* bar = (struct sample_wrapped_struct*) malloc(sizeof(struct sample_wrapped_struct));
bar->foo = 42; bar->foo = 42;
return Data_Wrap_Struct(klass, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar); return Data_Wrap_Struct(klass, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar);
} }
@ -32,7 +32,7 @@ VALUE sdaf_get_struct(VALUE self) {
} }
VALUE sws_wrap_struct(VALUE self, VALUE val) { VALUE sws_wrap_struct(VALUE self, VALUE val) {
struct sample_wrapped_struct* bar = malloc(sizeof(struct sample_wrapped_struct)); struct sample_wrapped_struct* bar = (struct sample_wrapped_struct*) malloc(sizeof(struct sample_wrapped_struct));
bar->foo = FIX2INT(val); bar->foo = FIX2INT(val);
return Data_Wrap_Struct(rb_cObject, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar); return Data_Wrap_Struct(rb_cObject, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar);
} }
@ -58,9 +58,9 @@ VALUE sws_get_struct_data_ptr(VALUE self, VALUE obj) {
VALUE sws_change_struct(VALUE self, VALUE obj, VALUE new_val) { VALUE sws_change_struct(VALUE self, VALUE obj, VALUE new_val) {
struct sample_wrapped_struct *old_struct, *new_struct; struct sample_wrapped_struct *old_struct, *new_struct;
new_struct = malloc(sizeof(struct sample_wrapped_struct)); new_struct = (struct sample_wrapped_struct*) malloc(sizeof(struct sample_wrapped_struct));
new_struct->foo = FIX2INT(new_val); new_struct->foo = FIX2INT(new_val);
old_struct = RDATA(obj)->data; old_struct = (struct sample_wrapped_struct*) RDATA(obj)->data;
free(old_struct); free(old_struct);
RDATA(obj)->data = new_struct; RDATA(obj)->data = new_struct;
return Qnil; return Qnil;

View file

@ -16,15 +16,15 @@ static VALUE registered_reference_address(VALUE self) {
return registered_reference_value; return registered_reference_value;
} }
static VALUE gc_spec_rb_gc_enable() { static VALUE gc_spec_rb_gc_enable(VALUE self) {
return rb_gc_enable(); return rb_gc_enable();
} }
static VALUE gc_spec_rb_gc_disable() { static VALUE gc_spec_rb_gc_disable(VALUE self) {
return rb_gc_disable(); return rb_gc_disable();
} }
static VALUE gc_spec_rb_gc() { static VALUE gc_spec_rb_gc(VALUE self) {
rb_gc(); rb_gc();
return Qnil; return Qnil;
} }

View file

@ -7,12 +7,16 @@ extern "C" {
VALUE g_hooked_var; VALUE g_hooked_var;
VALUE var_2x_getter(ID id, VALUE *data) {
return *data;
}
void var_2x_setter(VALUE val, ID id, VALUE *var) { void var_2x_setter(VALUE val, ID id, VALUE *var) {
*var = INT2NUM(NUM2INT(val) * 2); *var = INT2NUM(NUM2INT(val) * 2);
} }
static VALUE sb_define_hooked_variable(VALUE self, VALUE var_name) { static VALUE sb_define_hooked_variable(VALUE self, VALUE var_name) {
rb_define_hooked_variable(StringValuePtr(var_name), &g_hooked_var, 0, var_2x_setter); rb_define_hooked_variable(StringValuePtr(var_name), &g_hooked_var, var_2x_getter, var_2x_setter);
return Qnil; return Qnil;
} }

View file

@ -30,5 +30,5 @@ void Init_integer_spec(void) {
} }
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { }
#endif #endif

View file

@ -43,7 +43,7 @@ VALUE io_spec_rb_io_addstr(VALUE self, VALUE io, VALUE str) {
VALUE io_spec_rb_io_printf(VALUE self, VALUE io, VALUE ary) { VALUE io_spec_rb_io_printf(VALUE self, VALUE io, VALUE ary) {
long argc = RARRAY_LEN(ary); long argc = RARRAY_LEN(ary);
VALUE *argv = alloca(sizeof(VALUE) * argc); VALUE *argv = (VALUE*) alloca(sizeof(VALUE) * argc);
int i; int i;
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
@ -55,7 +55,7 @@ VALUE io_spec_rb_io_printf(VALUE self, VALUE io, VALUE ary) {
VALUE io_spec_rb_io_print(VALUE self, VALUE io, VALUE ary) { VALUE io_spec_rb_io_print(VALUE self, VALUE io, VALUE ary) {
long argc = RARRAY_LEN(ary); long argc = RARRAY_LEN(ary);
VALUE *argv = alloca(sizeof(VALUE) * argc); VALUE *argv = (VALUE*) alloca(sizeof(VALUE) * argc);
int i; int i;
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
@ -67,7 +67,7 @@ VALUE io_spec_rb_io_print(VALUE self, VALUE io, VALUE ary) {
VALUE io_spec_rb_io_puts(VALUE self, VALUE io, VALUE ary) { VALUE io_spec_rb_io_puts(VALUE self, VALUE io, VALUE ary) {
long argc = RARRAY_LEN(ary); long argc = RARRAY_LEN(ary);
VALUE *argv = alloca(sizeof(VALUE) * argc); VALUE *argv = (VALUE*) alloca(sizeof(VALUE) * argc);
int i; int i;
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {

View file

@ -68,7 +68,7 @@ VALUE rb_block_call_extra_data(VALUE self, VALUE object) {
} }
VALUE kernel_spec_rb_block_call_no_func(VALUE self, VALUE ary) { VALUE kernel_spec_rb_block_call_no_func(VALUE self, VALUE ary) {
return rb_block_call(ary, rb_intern("map"), 0, NULL, NULL, Qnil); return rb_block_call(ary, rb_intern("map"), 0, NULL, (rb_block_call_func_t)NULL, Qnil);
} }
@ -153,6 +153,10 @@ VALUE kernel_spec_rb_rescue(VALUE self, VALUE main_proc, VALUE arg,
rb_ary_push(main_array, main_proc); rb_ary_push(main_array, main_proc);
rb_ary_push(main_array, arg); rb_ary_push(main_array, arg);
if (raise_proc == Qnil) {
return rb_rescue(kernel_spec_call_proc, main_array, NULL, arg2);
}
raise_array = rb_ary_new(); raise_array = rb_ary_new();
rb_ary_push(raise_array, raise_proc); rb_ary_push(raise_array, raise_proc);
rb_ary_push(raise_array, arg2); rb_ary_push(raise_array, arg2);

View file

@ -0,0 +1,34 @@
#include "ruby.h"
#include "rubyspec.h"
#ifdef __cplusplus
extern "C" {
#endif
static VALUE language_spec_switch(VALUE self, VALUE value) {
if (value == ID2SYM(rb_intern("undef"))) {
value = Qundef;
}
switch (value) {
case Qtrue:
return ID2SYM(rb_intern("true"));
case Qfalse:
return ID2SYM(rb_intern("false"));
case Qnil:
return ID2SYM(rb_intern("nil"));
case Qundef:
return ID2SYM(rb_intern("undef"));
default:
return ID2SYM(rb_intern("default"));
}
}
void Init_language_spec(void) {
VALUE cls = rb_define_class("CApiLanguageSpecs", rb_cObject);
rb_define_method(cls, "switch", language_spec_switch, 1);
}
#ifdef __cplusplus
}
#endif

View file

@ -1,7 +1,15 @@
#include "ruby.h" #include "ruby.h"
#ifdef __cplusplus
extern "C" {
#endif
void Init_module_under_autoload_spec(void) { void Init_module_under_autoload_spec(void) {
VALUE specs = rb_const_get(rb_cObject, rb_intern("CApiModuleSpecs")); VALUE specs = rb_const_get(rb_cObject, rb_intern("CApiModuleSpecs"));
rb_define_module_under(specs, "ModuleUnderAutoload"); rb_define_module_under(specs, "ModuleUnderAutoload");
rb_define_module_under(specs, "RubyUnderAutoload"); rb_define_module_under(specs, "RubyUnderAutoload");
} }
#ifdef __cplusplus
}
#endif

View file

@ -61,6 +61,10 @@ static VALUE numeric_spec_NUM2LONG(VALUE self, VALUE num) {
return LONG2NUM(NUM2LONG(num)); return LONG2NUM(NUM2LONG(num));
} }
static VALUE numeric_spec_NUM2SHORT(VALUE self, VALUE num) {
return LONG2NUM(NUM2SHORT(num));
}
static VALUE numeric_spec_NUM2UINT(VALUE self, VALUE num) { static VALUE numeric_spec_NUM2UINT(VALUE self, VALUE num) {
return ULONG2NUM(NUM2UINT(num)); return ULONG2NUM(NUM2UINT(num));
} }
@ -109,6 +113,7 @@ void Init_numeric_spec(void) {
rb_define_method(cls, "NUM2DBL", numeric_spec_NUM2DBL, 1); rb_define_method(cls, "NUM2DBL", numeric_spec_NUM2DBL, 1);
rb_define_method(cls, "NUM2INT", numeric_spec_NUM2INT, 1); rb_define_method(cls, "NUM2INT", numeric_spec_NUM2INT, 1);
rb_define_method(cls, "NUM2LONG", numeric_spec_NUM2LONG, 1); rb_define_method(cls, "NUM2LONG", numeric_spec_NUM2LONG, 1);
rb_define_method(cls, "NUM2SHORT", numeric_spec_NUM2SHORT, 1);
rb_define_method(cls, "INT2NUM", numeric_spec_INT2NUM, 1); rb_define_method(cls, "INT2NUM", numeric_spec_INT2NUM, 1);
rb_define_method(cls, "NUM2UINT", numeric_spec_NUM2UINT, 1); rb_define_method(cls, "NUM2UINT", numeric_spec_NUM2UINT, 1);
rb_define_method(cls, "NUM2ULONG", numeric_spec_NUM2ULONG, 1); rb_define_method(cls, "NUM2ULONG", numeric_spec_NUM2ULONG, 1);

View file

@ -102,7 +102,7 @@ static VALUE so_rb_obj_dup(VALUE self, VALUE klass) {
static VALUE so_rb_obj_call_init(VALUE self, VALUE object, static VALUE so_rb_obj_call_init(VALUE self, VALUE object,
VALUE nargs, VALUE args) { VALUE nargs, VALUE args) {
int c_nargs = FIX2INT(nargs); int c_nargs = FIX2INT(nargs);
VALUE *c_args = alloca(sizeof(VALUE) * c_nargs); VALUE *c_args = (VALUE*) alloca(sizeof(VALUE) * c_nargs);
int i; int i;
for (i = 0; i < c_nargs; i++) for (i = 0; i < c_nargs; i++)

View file

@ -23,6 +23,10 @@ VALUE proc_spec_rb_proc_call(VALUE self, VALUE prc, VALUE args) {
return rb_proc_call(prc, args); return rb_proc_call(prc, args);
} }
VALUE proc_spec_rb_obj_is_proc(VALUE self, VALUE prc) {
return rb_obj_is_proc(prc);
}
/* This helper is not strictly necessary but reflects the code in wxRuby that /* This helper is not strictly necessary but reflects the code in wxRuby that
* originally exposed issues with this Proc.new behavior. * originally exposed issues with this Proc.new behavior.
*/ */
@ -61,6 +65,7 @@ void Init_proc_spec(void) {
rb_define_method(cls, "rb_proc_arity", proc_spec_rb_proc_arity, 1); 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", proc_spec_rb_proc_call, 2);
rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1); 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);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -23,6 +23,10 @@
(RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR < (minor)) || \ (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR < (minor)) || \
(RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR == (minor) && RUBY_VERSION_TEENY < (teeny))) (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR == (minor) && RUBY_VERSION_TEENY < (teeny)))
#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 7)
#define RUBY_VERSION_IS_2_7
#endif
#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 6) #if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 6)
#define RUBY_VERSION_IS_2_6 #define RUBY_VERSION_IS_2_6
#endif #endif
@ -31,4 +35,26 @@
#define RUBY_VERSION_IS_2_4 #define RUBY_VERSION_IS_2_4
#endif #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)
#endif
#endif #endif

View file

@ -284,6 +284,26 @@ VALUE string_spec_RSTRING_PTR_iterate(VALUE self, VALUE str) {
return Qnil; return Qnil;
} }
VALUE string_spec_RSTRING_PTR_iterate_uint32(VALUE self, VALUE str) {
int i;
uint32_t* ptr;
int l = RSTRING_LEN(str) / sizeof(uint32_t);
ptr = (uint32_t *)RSTRING_PTR(str);
for(i = 0; i < l; i++) {
rb_yield(UINT2NUM(ptr[i]));
}
return Qnil;
}
VALUE string_spec_RSTRING_PTR_short_memcpy(VALUE self, VALUE str) {
// Short memcpy operations may be optimised by the compiler to a single write.
if (RSTRING_LEN(str) >= 8) {
memcpy(RSTRING_PTR(str), "Infinity", 8);
}
return str;
}
VALUE string_spec_RSTRING_PTR_assign(VALUE self, VALUE str, VALUE chr) { VALUE string_spec_RSTRING_PTR_assign(VALUE self, VALUE str, VALUE chr) {
int i; int i;
char c; char c;
@ -362,11 +382,11 @@ static VALUE string_spec_rb_sprintf2(VALUE self, VALUE str, VALUE repl1, VALUE r
} }
static VALUE string_spec_rb_sprintf3(VALUE self, VALUE str) { static VALUE string_spec_rb_sprintf3(VALUE self, VALUE str) {
return rb_sprintf("Result: %"PRIsVALUE".", str); return rb_sprintf("Result: %" PRIsVALUE ".", str);
} }
static VALUE string_spec_rb_sprintf4(VALUE self, VALUE str) { static VALUE string_spec_rb_sprintf4(VALUE self, VALUE str) {
return rb_sprintf("Result: %+"PRIsVALUE".", str); return rb_sprintf("Result: %+" PRIsVALUE ".", str);
} }
static VALUE string_spec_rb_vsprintf_worker(char* fmt, ...) { static VALUE string_spec_rb_vsprintf_worker(char* fmt, ...) {
@ -477,6 +497,8 @@ void Init_string_spec(void) {
rb_define_method(cls, "RSTRING_LEN", string_spec_RSTRING_LEN, 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_LENINT", string_spec_RSTRING_LENINT, 1);
rb_define_method(cls, "RSTRING_PTR_iterate", string_spec_RSTRING_PTR_iterate, 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);
rb_define_method(cls, "RSTRING_PTR_assign", string_spec_RSTRING_PTR_assign, 2); rb_define_method(cls, "RSTRING_PTR_assign", string_spec_RSTRING_PTR_assign, 2);
rb_define_method(cls, "RSTRING_PTR_set", string_spec_RSTRING_PTR_set, 3); 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_funcall", string_spec_RSTRING_PTR_after_funcall, 2);

View file

@ -7,6 +7,10 @@
extern "C" { extern "C" {
#endif #endif
VALUE symbol_spec_SYMBOL_P(VALUE self, VALUE obj) {
return SYMBOL_P(obj) ? Qtrue : Qfalse;
}
VALUE symbol_spec_rb_intern(VALUE self, VALUE string) { VALUE symbol_spec_rb_intern(VALUE self, VALUE string) {
return ID2SYM(rb_intern(RSTRING_PTR(string))); return ID2SYM(rb_intern(RSTRING_PTR(string)));
} }
@ -73,6 +77,7 @@ VALUE symbol_spec_rb_sym2str(VALUE self, VALUE sym) {
void Init_symbol_spec(void) { void Init_symbol_spec(void) {
VALUE cls = rb_define_class("CApiSymbolSpecs", rb_cObject); VALUE cls = rb_define_class("CApiSymbolSpecs", rb_cObject);
rb_define_method(cls, "SYMBOL_P", symbol_spec_SYMBOL_P, 1);
rb_define_method(cls, "rb_intern", symbol_spec_rb_intern, 1); rb_define_method(cls, "rb_intern", symbol_spec_rb_intern, 1);
rb_define_method(cls, "rb_intern2", symbol_spec_rb_intern2, 2); rb_define_method(cls, "rb_intern2", symbol_spec_rb_intern2, 2);
rb_define_method(cls, "rb_intern_const", symbol_spec_rb_intern_const, 1); rb_define_method(cls, "rb_intern_const", symbol_spec_rb_intern_const, 1);

View file

@ -15,7 +15,7 @@
extern "C" { extern "C" {
#endif #endif
static VALUE thread_spec_rb_thread_alone() { static VALUE thread_spec_rb_thread_alone(VALUE self) {
return rb_thread_alone() ? Qtrue : Qfalse; return rb_thread_alone() ? Qtrue : Qfalse;
} }
@ -89,7 +89,7 @@ static VALUE thread_spec_rb_thread_call_without_gvl_with_ubf_io(VALUE self) {
return (VALUE)ret; return (VALUE)ret;
} }
static VALUE thread_spec_rb_thread_current() { static VALUE thread_spec_rb_thread_current(VALUE self) {
return rb_thread_current(); return rb_thread_current();
} }

View file

@ -86,7 +86,8 @@ static const rb_data_type_t sample_typed_wrapped_struct_other_data_type = {
VALUE sdaf_alloc_typed_func(VALUE klass) { VALUE sdaf_alloc_typed_func(VALUE klass) {
struct sample_typed_wrapped_struct* bar = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct)); struct sample_typed_wrapped_struct* bar;
bar = (struct sample_typed_wrapped_struct *) malloc(sizeof(struct sample_typed_wrapped_struct));
bar->foo = 42; bar->foo = 42;
return TypedData_Wrap_Struct(klass, &sample_typed_wrapped_struct_data_type, bar); return TypedData_Wrap_Struct(klass, &sample_typed_wrapped_struct_data_type, bar);
} }
@ -99,7 +100,8 @@ VALUE sdaf_typed_get_struct(VALUE self) {
} }
VALUE sws_typed_wrap_struct(VALUE self, VALUE val) { VALUE sws_typed_wrap_struct(VALUE self, VALUE val) {
struct sample_typed_wrapped_struct* bar = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct)); struct sample_typed_wrapped_struct* bar;
bar = (struct sample_typed_wrapped_struct *) malloc(sizeof(struct sample_typed_wrapped_struct));
bar->foo = FIX2INT(val); bar->foo = FIX2INT(val);
return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar); return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar);
} }
@ -138,11 +140,10 @@ VALUE sws_typed_get_struct_data_ptr(VALUE self, VALUE obj) {
} }
VALUE sws_typed_change_struct(VALUE self, VALUE obj, VALUE new_val) { VALUE sws_typed_change_struct(VALUE self, VALUE obj, VALUE new_val) {
struct sample_typed_wrapped_struct *old_struct, *new_struct; struct sample_typed_wrapped_struct *new_struct;
new_struct = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct)); new_struct = (struct sample_typed_wrapped_struct *) malloc(sizeof(struct sample_typed_wrapped_struct));
new_struct->foo = FIX2INT(new_val); new_struct->foo = FIX2INT(new_val);
old_struct = RTYPEDDATA(obj)->data; free(RTYPEDDATA(obj)->data);
free(old_struct);
RTYPEDDATA(obj)->data = new_struct; RTYPEDDATA(obj)->data = new_struct;
return Qnil; return Qnil;
} }

View file

@ -60,8 +60,8 @@ static VALUE util_spec_rb_get_kwargs(VALUE self, VALUE keyword_hash, VALUE keys,
int values_len = req + (opt < 0 ? -1 - opt : opt); int values_len = req + (opt < 0 ? -1 - opt : opt);
int i = 0; int i = 0;
ID *ids = malloc(sizeof(VALUE) * len); ID *ids = (ID*) malloc(sizeof(VALUE) * len);
VALUE *results = malloc(sizeof(VALUE) * values_len); VALUE *results = (VALUE*) malloc(sizeof(VALUE) * values_len);
int extracted = 0; int extracted = 0;
VALUE ary = Qundef; VALUE ary = Qundef;

View file

@ -261,28 +261,28 @@ describe "C-API Kernel function" do
describe "rb_protect" do describe "rb_protect" do
it "will run a function with an argument" do it "will run a function with an argument" do
proof = [] # Hold proof of work performed after the yield. proof = [] # Hold proof of work performed after the yield.
res = @s.rb_protect_yield(7, proof) { |x| x + 1 } res = @s.rb_protect_yield(77, proof) { |x| x + 1 }
res.should == 8 res.should == 78
proof[0].should == 23 proof[0].should == 23
end end
it "will allow cleanup code to run after break" do it "will allow cleanup code to run after break" do
proof = [] # Hold proof of work performed after the yield. proof = [] # Hold proof of work performed after the yield.
@s.rb_protect_yield(7, proof) { |x| break } @s.rb_protect_yield(77, proof) { |x| break }
proof[0].should == 23 proof[0].should == 23
end end
it "will allow cleanup code to run after break with value" do it "will allow cleanup code to run after break with value" do
proof = [] # Hold proof of work performed after the yield. proof = [] # Hold proof of work performed after the yield.
res = @s.rb_protect_yield(7, proof) { |x| break x + 1 } res = @s.rb_protect_yield(77, proof) { |x| break x + 1 }
res.should == 8 res.should == 78
proof[0].should == 23 proof[0].should == 23
end end
it "will allow cleanup code to run after a raise" do it "will allow cleanup code to run after a raise" do
proof = [] # Hold proof of work performed after the yield. proof = [] # Hold proof of work performed after the yield.
-> do -> do
@s.rb_protect_yield(7, proof) { |x| raise NameError} @s.rb_protect_yield(77, proof) { |x| raise NameError}
end.should raise_error(NameError) end.should raise_error(NameError)
proof[0].should == 23 proof[0].should == 23
end end
@ -290,7 +290,7 @@ describe "C-API Kernel function" do
it "will return nil if an error was raised" do it "will return nil if an error was raised" do
proof = [] # Hold proof of work performed after the yield. proof = [] # Hold proof of work performed after the yield.
-> do -> do
@s.rb_protect_yield(7, proof) { |x| raise NameError} @s.rb_protect_yield(77, proof) { |x| raise NameError}
end.should raise_error(NameError) end.should raise_error(NameError)
proof[0].should == 23 proof[0].should == 23
proof[1].should == nil proof[1].should == nil
@ -316,47 +316,47 @@ describe "C-API Kernel function" do
describe "rb_rescue" do describe "rb_rescue" do
before :each do before :each do
@proc = -> x { x } @proc = -> x { x }
@raise_proc_returns_sentinel = -> *_ { :raise_proc_executed } @rescue_proc_returns_sentinel = -> *_ { :rescue_proc_executed }
@raise_proc_returns_arg = -> *a { a } @rescue_proc_returns_arg = -> *a { a }
@arg_error_proc = -> *_ { raise ArgumentError, '' } @arg_error_proc = -> *_ { raise ArgumentError, '' }
@std_error_proc = -> *_ { raise StandardError, '' } @std_error_proc = -> *_ { raise StandardError, '' }
@exc_error_proc = -> *_ { raise Exception, '' } @exc_error_proc = -> *_ { raise Exception, '' }
end end
it "executes passed function" do it "executes passed function" do
@s.rb_rescue(@proc, :no_exc, @raise_proc_returns_arg, :exc).should == :no_exc @s.rb_rescue(@proc, :no_exc, @rescue_proc_returns_arg, :exc).should == :no_exc
end end
it "executes passed 'raise function' if a StandardError exception is raised" do it "executes the passed 'rescue function' if a StandardError exception is raised" do
@s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_sentinel, :exc).should == :raise_proc_executed @s.rb_rescue(@arg_error_proc, nil, @rescue_proc_returns_sentinel, :exc).should == :rescue_proc_executed
@s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_sentinel, :exc).should == :raise_proc_executed @s.rb_rescue(@std_error_proc, nil, @rescue_proc_returns_sentinel, :exc).should == :rescue_proc_executed
end end
it "passes the user supplied argument to the 'raise function' if a StandardError exception is raised" do it "passes the user supplied argument to the 'rescue function' if a StandardError exception is raised" do
arg1, _ = @s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_arg, :exc1) arg1, _ = @s.rb_rescue(@arg_error_proc, nil, @rescue_proc_returns_arg, :exc1)
arg1.should == :exc1 arg1.should == :exc1
arg2, _ = @s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_arg, :exc2) arg2, _ = @s.rb_rescue(@std_error_proc, nil, @rescue_proc_returns_arg, :exc2)
arg2.should == :exc2 arg2.should == :exc2
end end
it "passes the raised exception to the 'raise function' if a StandardError exception is raised" do it "passes the raised exception to the 'rescue function' if a StandardError exception is raised" do
_, exc1 = @s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_arg, :exc) _, exc1 = @s.rb_rescue(@arg_error_proc, nil, @rescue_proc_returns_arg, :exc)
exc1.class.should == ArgumentError exc1.class.should == ArgumentError
_, exc2 = @s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_arg, :exc) _, exc2 = @s.rb_rescue(@std_error_proc, nil, @rescue_proc_returns_arg, :exc)
exc2.class.should == StandardError exc2.class.should == StandardError
end end
it "raises an exception if passed function raises an exception other than StandardError" do it "raises an exception if passed function raises an exception other than StandardError" do
-> { @s.rb_rescue(@exc_error_proc, nil, @raise_proc_returns_arg, nil) }.should raise_error(Exception) -> { @s.rb_rescue(@exc_error_proc, nil, @rescue_proc_returns_arg, nil) }.should raise_error(Exception)
end end
it "raises an exception if any exception is raised inside 'raise function'" do 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_error(StandardError)
end end
it "makes $! available only during 'raise function' execution" do it "makes $! available only during the 'rescue function' execution" do
@s.rb_rescue(@std_error_proc, nil, -> *_ { $! }, nil).class.should == StandardError @s.rb_rescue(@std_error_proc, nil, -> *_ { $! }, nil).class.should == StandardError
$!.should == nil $!.should == nil
end end
@ -368,6 +368,10 @@ describe "C-API Kernel function" do
proc_caller { break :value }.should == :value proc_caller { break :value }.should == :value
end end
it "returns nil if the 'rescue function' is null" do
@s.rb_rescue(@std_error_proc, nil, nil, nil).should == nil
end
end end
describe "rb_rescue2" do describe "rb_rescue2" do

View file

@ -0,0 +1,31 @@
require_relative 'spec_helper'
load_extension("language")
describe "C language construct" do
before :each do
@s = CApiLanguageSpecs.new
end
describe "switch (VALUE)" do
it "works for Qtrue" do
@s.switch(true).should == :true
end
it "works for Qfalse" do
@s.switch(false).should == :false
end
it "works for Qnil" do
@s.switch(nil).should == :nil
end
it "works for Qundef" do
@s.switch(:undef).should == :undef
end
it "works for the default case" do
@s.switch(Object.new).should == :default
end
end
end

View file

@ -143,6 +143,34 @@ describe "CApiNumericSpecs" do
end end
end end
describe "NUM2SHORT" do
it "raises a TypeError if passed nil" do
-> { @s.NUM2SHORT(nil) }.should raise_error(TypeError)
end
it "converts a Float" do
@s.NUM2SHORT(4.2).should == 4
end
it "converts a Fixnum" do
@s.NUM2SHORT(5).should == 5
end
it "converts -1 to an signed number" do
@s.NUM2SHORT(-1).should == -1
end
it "raises a RangeError if the value is more than 32bits" do
-> { @s.NUM2SHORT(0xffff_ffff+1) }.should raise_error(RangeError)
end
it "calls #to_int to coerce the value" do
obj = mock("number")
obj.should_receive(:to_int).and_return(2)
@s.NUM2SHORT(obj).should == 2
end
end
describe "INT2NUM" do describe "INT2NUM" do
it "raises a TypeError if passed nil" do it "raises a TypeError if passed nil" do
-> { @s.INT2NUM(nil) }.should raise_error(TypeError) -> { @s.INT2NUM(nil) }.should raise_error(TypeError)

View file

@ -54,6 +54,25 @@ describe "C-API Proc function" do
@p.rb_proc_call(prc, [6, 7]).should == 42 @p.rb_proc_call(prc, [6, 7]).should == 42
end end
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
end
it "returns true for subclass of Proc" do
prc = Class.new(Proc).new {}
@p.rb_obj_is_proc(prc).should be_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
end
end
end end
describe "C-API when calling Proc.new from a C function" do describe "C-API when calling Proc.new from a C function" do

View file

@ -8,12 +8,17 @@ require 'rbconfig'
OBJDIR ||= File.expand_path("../../../ext/#{RUBY_ENGINE}/#{RUBY_VERSION}", __FILE__) OBJDIR ||= File.expand_path("../../../ext/#{RUBY_ENGINE}/#{RUBY_VERSION}", __FILE__)
def object_path def object_path
mkdir_p(OBJDIR) path = OBJDIR
OBJDIR if ENV['SPEC_CAPI_CXX'] == 'true'
path = "#{path}/cxx"
end
mkdir_p(path)
path
end end
def compile_extension(name) def compile_extension(name)
debug = false debug = false
cxx = ENV['SPEC_CAPI_CXX'] == 'true'
run_mkmf_in_process = RUBY_ENGINE == 'truffleruby' run_mkmf_in_process = RUBY_ENGINE == 'truffleruby'
core_ext_dir = File.expand_path("../ext", __FILE__) core_ext_dir = File.expand_path("../ext", __FILE__)
@ -54,8 +59,12 @@ def compile_extension(name)
Dir.mkdir(tmpdir) Dir.mkdir(tmpdir)
begin begin
["#{core_ext_dir}/rubyspec.h", "#{spec_ext_dir}/#{ext}.c"].each do |file| ["#{core_ext_dir}/rubyspec.h", "#{spec_ext_dir}/#{ext}.c"].each do |file|
if cxx and file.end_with?('.c')
cp file, "#{tmpdir}/#{File.basename(file, '.c')}.cpp"
else
cp file, "#{tmpdir}/#{File.basename(file)}" cp file, "#{tmpdir}/#{File.basename(file)}"
end end
end
Dir.chdir(tmpdir) do Dir.chdir(tmpdir) do
if run_mkmf_in_process if run_mkmf_in_process
@ -64,11 +73,13 @@ def compile_extension(name)
init_mkmf unless required init_mkmf unless required
create_makefile(ext, tmpdir) create_makefile(ext, tmpdir)
else else
File.write("extconf.rb", "require 'mkmf'\n" + File.write("extconf.rb", <<-RUBY)
"$ruby = ENV.values_at('RUBY_EXE', 'RUBY_FLAGS').join(' ')\n" + require 'mkmf'
$ruby = ENV.values_at('RUBY_EXE', 'RUBY_FLAGS').join(' ')
# MRI magic to consider building non-bundled extensions # MRI magic to consider building non-bundled extensions
"$extout = nil\n" + $extout = nil
"create_makefile(#{ext.inspect})\n") create_makefile(#{ext.inspect})
RUBY
output = ruby_exe("extconf.rb") output = ruby_exe("extconf.rb")
raise "extconf failed:\n#{output}" unless $?.success? raise "extconf failed:\n#{output}" unless $?.success?
$stderr.puts output if debug $stderr.puts output if debug

View file

@ -513,6 +513,24 @@ describe "C-API String function" do
end end
chars.should == [55, 48, 227, 131, 145, 227, 130, 175] chars.should == [55, 48, 227, 131, 145, 227, 130, 175]
end end
it "returns a pointer which can be cast and used as another type" do
s = "70パク".
encode(Encoding::UTF_16LE).
force_encoding(Encoding::UTF_16LE).
encode(Encoding::UTF_8)
ints = []
@s.RSTRING_PTR_iterate_uint32(s) do |i|
ints << i
end
ints.should == s.unpack('LL')
end
it "allows a short memcpy to the string which may be converted to a single write operation by the compiler" do
str = " "
@s.RSTRING_PTR_short_memcpy(str).should == "Infinity"
end
end end
describe "RSTRING_LEN" do describe "RSTRING_LEN" do

View file

@ -8,6 +8,16 @@ describe "C-API Symbol function" do
@s = CApiSymbolSpecs.new @s = CApiSymbolSpecs.new
end end
describe "SYMBOL_P" do
it "returns true for a Symbol" do
@s.SYMBOL_P(:foo).should == true
end
it "returns false for non-Symbols" do
@s.SYMBOL_P('bar').should == false
end
end
describe "rb_intern" do describe "rb_intern" do
it "converts a string to a symbol, uniquely" do it "converts a string to a symbol, uniquely" do
@s.rb_intern("test_symbol").should == :test_symbol @s.rb_intern("test_symbol").should == :test_symbol