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 2021-01-28 17:08:57 +01:00
parent 1b377b32c8
commit 2e32b919b4
35 changed files with 832 additions and 10 deletions

View file

@ -29,4 +29,13 @@ describe "Integer#digits" do
it "raises Math::DomainError when calling digits on a negative number" do
-> { -12345.digits(7) }.should raise_error(Math::DomainError)
end
it "returns integer values > 9 when base is above 10" do
1234.digits(16).should == [2, 13, 4]
end
it "can be used with base > 37" do
1234.digits(100).should == [34, 12]
980099.digits(100).should == [99, 0, 98]
end
end

View file

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

View file

@ -52,6 +52,14 @@ describe 'Kernel#caller' do
end
end
ruby_version_is "2.7" do
it "works with beginless ranges" do
locations1 = KernelSpecs::CallerTest.locations(0)
locations2 = KernelSpecs::CallerTest.locations(eval("(..5)"))
locations2.map(&:to_s)[eval("(2..)")].should == locations1[eval("(..5)")].map(&:to_s)[eval("(2..)")]
end
end
guard -> { Kernel.instance_method(:tap).source_location } do
it "includes core library methods defined in Ruby" do
file, line = Kernel.instance_method(:tap).source_location

View file

@ -123,4 +123,16 @@ describe "Kernel.lambda" do
it "allows long returns to flow through it" do
KernelSpecs::Lambda.new.outer.should == :good
end
it "treats the block as a Proc when lambda is re-defined" do
klass = Class.new do
def lambda (&block); block; end
def ret
lambda { return 1 }.call
2
end
end
klass.new.lambda { 42 }.should be_an_instance_of Proc
klass.new.ret.should == 1
end
end

View file

@ -117,6 +117,48 @@ describe "Kernel.rand" do
end
end
context "given an inclusive range between 0 and 1" do
it "returns an Integer between the two Integers" do
x = rand(0..1)
x.should be_kind_of(Integer)
(0..1).should include(x)
end
it "returns a Float if at least one side is Float" do
seed = 42
x1 = Random.new(seed).rand(0..1.0)
x2 = Random.new(seed).rand(0.0..1.0)
x3 = Random.new(seed).rand(0.0..1)
x3.should be_kind_of(Float)
x1.should equal(x3)
x2.should equal(x3)
(0.0..1.0).should include(x3)
end
end
context "given an exclusive range between 0 and 1" do
it "returns zero as an Integer" do
x = rand(0...1)
x.should be_kind_of(Integer)
x.should eql(0)
end
it "returns a Float if at least one side is Float" do
seed = 42
x1 = Random.new(seed).rand(0...1.0)
x2 = Random.new(seed).rand(0.0...1.0)
x3 = Random.new(seed).rand(0.0...1)
x3.should be_kind_of(Float)
x1.should equal(x3)
x2.should equal(x3)
(0.0...1.0).should include(x3)
end
end
it "returns a numeric for an range argument where max is < 1" do
rand(0.25..0.75).should be_kind_of(Numeric)
end

View file

@ -0,0 +1,112 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is "2.7" do
describe "Module#ruby2_keywords" do
it "marks the final hash argument as keyword hash" do
obj = Object.new
obj.singleton_class.class_exec do
def foo(*a) a.last end
ruby2_keywords :foo
end
last = obj.foo(1, 2, a: "a")
Hash.ruby2_keywords_hash?(last).should == true
end
ruby_version_is "2.7" ... "3.0" do
it "fixes delegation warnings when calling a method accepting keywords" do
obj = Object.new
obj.singleton_class.class_exec do
def foo(*a) bar(*a) end
def bar(*a, **b) end
end
-> { obj.foo(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
obj.singleton_class.class_exec do
ruby2_keywords :foo
end
-> { obj.foo(1, 2, {a: "a"}) }.should_not complain
end
end
it "returns nil" do
obj = Object.new
obj.singleton_class.class_exec do
def foo(*a) end
ruby2_keywords(:foo).should == nil
end
end
it "raises NameError when passed not existing method name" do
obj = Object.new
-> {
obj.singleton_class.class_exec do
ruby2_keywords :not_existing
end
}.should raise_error(NameError, /undefined method `not_existing'/)
end
it "acceps String as well" do
obj = Object.new
obj.singleton_class.class_exec do
def foo(*a) a.last end
ruby2_keywords "foo"
end
last = obj.foo(1, 2, a: "a")
Hash.ruby2_keywords_hash?(last).should == true
end
it "raises TypeError when passed not Symbol or String" do
obj = Object.new
-> {
obj.singleton_class.class_exec do
ruby2_keywords Object.new
end
}.should raise_error(TypeError, /is not a symbol nor a string/)
end
it "prints warning when a method does not accept argument splat" do
obj = Object.new
def obj.foo(a, b, c) end
-> {
obj.singleton_class.class_exec do
ruby2_keywords :foo
end
}.should complain(/Skipping set of ruby2_keywords flag for/)
end
it "prints warning when a method accepts keywords" do
obj = Object.new
def obj.foo(a:, b:) end
-> {
obj.singleton_class.class_exec do
ruby2_keywords :foo
end
}.should complain(/Skipping set of ruby2_keywords flag for/)
end
it "prints warning when a method accepts keyword splat" do
obj = Object.new
def obj.foo(**a) end
-> {
obj.singleton_class.class_exec do
ruby2_keywords :foo
end
}.should complain(/Skipping set of ruby2_keywords flag for/)
end
end
end

View file

@ -42,4 +42,27 @@ describe "Module#to_s" do
obj = ModuleSpecs::NamedClass.new
obj.singleton_class.to_s.should =~ /\A#<Class:#<ModuleSpecs::NamedClass:0x\h+>>\z/
end
it "always show the refinement name, even if the module is named" do
module ModuleSpecs::RefinementInspect
R = refine String do
end
end
ModuleSpecs::RefinementInspect::R.name.should == 'ModuleSpecs::RefinementInspect::R'
ModuleSpecs::RefinementInspect::R.to_s.should == '#<refinement:String@ModuleSpecs::RefinementInspect>'
end
it 'does not call #inspect or #to_s for singleton classes' do
klass = Class.new
obj = klass.new
def obj.to_s
"to_s"
end
def obj.inspect
"inspect"
end
sclass = obj.singleton_class
sclass.to_s.should =~ /\A#<Class:#<#{Regexp.escape klass.to_s}:0x\h+>>\z/
end
end

View file

@ -0,0 +1,64 @@
require_relative '../../spec_helper'
ruby_version_is "2.7" do
describe "Proc#ruby2_keywords" do
it "marks the final hash argument as keyword hash" do
f = -> *a { a.last }
f.ruby2_keywords
last = f.call(1, 2, a: "a")
Hash.ruby2_keywords_hash?(last).should == true
end
ruby_version_is "2.7" ... "3.0" do
it "fixes delegation warnings when calling a method accepting keywords" do
obj = Object.new
def obj.foo(*a, **b) end
f = -> *a { obj.foo(*a) }
-> { f.call(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
f.ruby2_keywords
-> { f.call(1, 2, {a: "a"}) }.should_not complain
end
it "fixes delegation warnings when calling a proc accepting keywords" do
g = -> *a, **b { }
f = -> *a { g.call(*a) }
-> { f.call(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
f.ruby2_keywords
-> { f.call(1, 2, {a: "a"}) }.should_not complain
end
end
it "returns self" do
f = -> *a { }
f.ruby2_keywords.should equal f
end
it "prints warning when a proc does not accept argument splat" do
f = -> a, b, c { }
-> {
f.ruby2_keywords
}.should complain(/Skipping set of ruby2_keywords flag for/)
end
it "prints warning when a proc accepts keywords" do
f = -> a:, b: { }
-> {
f.ruby2_keywords
}.should complain(/Skipping set of ruby2_keywords flag for/)
end
it "prints warning when a proc accepts keyword splat" do
f = -> **a { }
-> {
f.ruby2_keywords
}.should complain(/Skipping set of ruby2_keywords flag for/)
end
end
end

View file

@ -152,4 +152,46 @@ describe :range_cover_subrange, shared: true do
end
end
end
ruby_version_is "2.7" do
it "allows self to be a beginless range" do
eval("(...10)").send(@method, (3..7)).should be_true
eval("(...10)").send(@method, (3..15)).should be_false
eval("(..7.9)").send(@method, (2.5..6.5)).should be_true
eval("(..7.9)").send(@method, (2.5..8.5)).should be_false
eval("(..'i')").send(@method, ('d'..'f')).should be_true
eval("(..'i')").send(@method, ('d'..'z')).should be_false
end
it "allows self to be a endless range" do
eval("(0...)").send(@method, (3..7)).should be_true
eval("(5...)").send(@method, (3..15)).should be_false
eval("(1.1..)").send(@method, (2.5..6.5)).should be_true
eval("(3.3..)").send(@method, (2.5..8.5)).should be_false
eval("('a'..)").send(@method, ('d'..'f')).should be_true
eval("('p'..)").send(@method, ('d'..'z')).should be_false
end
it "accepts beginless range argument" do
eval("(..10)").send(@method, eval("(...10)")).should be_true
(0..10).send(@method, eval("(...10)")).should be_false
(1.1..7.9).send(@method, eval("(...10.5)")).should be_false
('c'..'i').send(@method, eval("(..'i')")).should be_false
end
it "accepts endless range argument" do
eval("(0..)").send(@method, eval("(0...)")).should be_true
(0..10).send(@method, eval("(0...)")).should be_false
(1.1..7.9).send(@method, eval("(0.8...)")).should be_false
('c'..'i').send(@method, eval("('a'..)")).should be_false
end
end
end

View file

@ -26,6 +26,13 @@ describe :range_cover_and_include, shared: true do
end
end
ruby_version_is "2.7" do
it "returns true if other is an element of self for beginless ranges" do
eval("(..10)").send(@method, 2.4).should == true
eval("(...10.5)").send(@method, 2.4).should == true
end
end
it "compares values using <=>" do
rng = (1..5)
m = mock("int")

View file

@ -16,6 +16,11 @@ describe "Range#to_a" do
(0xffff...0xfffd).to_a.should == []
end
it "works with Ranges of 64-bit integers" do
large = 1 << 40
(large..large+1).to_a.should == [1099511627776, 1099511627777]
end
it "works with Ranges of Symbols" do
(:A..:z).to_a.size.should == 58
end

View file

@ -18,6 +18,13 @@ describe "Range#to_s" do
end
end
ruby_version_is "2.7" do
it "can show beginless ranges" do
eval("(..1)").to_s.should == "..1"
eval("(...1.0)").to_s.should == "...1.0"
end
end
ruby_version_is ''...'2.7' do
it "returns a tainted string if either end is tainted" do
(("a".taint)..."c").to_s.tainted?.should be_true

View file

@ -39,6 +39,13 @@ describe "String#scrub with a custom replacement" do
"abc\u3042#{x81}".scrub("*").should == "abc\u3042*"
end
it "replaces invalid byte sequences in frozen strings" do
x81 = [0x81].pack('C').force_encoding('utf-8')
(-"abc\u3042#{x81}").scrub("*").should == "abc\u3042*"
utf16_str = ("abc".encode('UTF-16LE').bytes + [0x81]).pack('c*').force_encoding('UTF-16LE')
(-(utf16_str)).scrub("*".encode('UTF-16LE')).should == "abc*".encode('UTF-16LE')
end
it "replaces an incomplete character at the end with a single replacement" do
xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8'
xE3x80.scrub("*").should == "*"

View file

@ -335,6 +335,16 @@ describe :string_slice_range, shared: true do
"hello there".send(@method, eval("(-4...)")).should == "here"
end
end
ruby_version_is "2.7" do
it "works with beginless ranges" do
"hello there".send(@method, eval("(..5)")).should == "hello "
"hello there".send(@method, eval("(...5)")).should == "hello"
"hello there".send(@method, eval("(..-4)")).should == "hello th"
"hello there".send(@method, eval("(...-4)")).should == "hello t"
"hello there".send(@method, eval("(...nil)")).should == "hello there"
end
end
end
describe :string_slice_regexp, shared: true do

View file

@ -471,6 +471,14 @@ describe "String#split with Regexp" do
a.should == ["Chunky", "Bacon"]
end
it "yields each split substring with default pattern for a non-ASCII string" do
a = []
returned_object = "l'été arrive bientôt".split { |str| a << str }
returned_object.should == "l'été arrive bientôt"
a.should == ["l'été", "arrive", "bientôt"]
end
it "yields the string when limit is 1" do
a = []
returned_object = "chunky bacon".split("", 1) { |str| a << str.capitalize }

View file

@ -56,5 +56,9 @@ describe "Struct#hash" do
# See the Struct#eql? specs
end
it "returns different hashes for different struct classes" do
Struct.new(:x).new(1).hash.should != Struct.new(:y).new(1).hash
end
it_behaves_like :struct_accessor, :hash
end

View file

@ -0,0 +1,19 @@
require_relative '../../spec_helper'
ruby_version_is "3.0" do
describe "Symbol#name" do
it "returns string" do
:ruby.name.should == "ruby"
:.name.should == "ルビー"
end
it "returns same string instance" do
:"ruby_3".name.should.equal?(:ruby_3.name)
:"ruby_#{1+2}".name.should.equal?(:ruby_3.name)
end
it "returns frozen string" do
:symbol.name.should.frozen?
end
end
end

View file

@ -51,6 +51,14 @@ describe "Thread#backtrace_locations" do
end
end
ruby_version_is "2.7" do
it "can be called with an beginless range" do
locations1 = Thread.current.backtrace_locations(0)
locations2 = Thread.current.backtrace_locations(eval("(..5)"))
locations2.map(&:to_s)[eval("(2..)")].should == locations1[eval("(..5)")].map(&:to_s)[eval("(2..)")]
end
end
it "returns nil if omitting more locations than available" do
Thread.current.backtrace_locations(100).should == nil
Thread.current.backtrace_locations(100..-1).should == nil

View file

@ -0,0 +1,125 @@
require_relative '../../spec_helper'
describe "Thread.handle_interrupt" do
def make_handle_interrupt_thread(interrupt_config, blocking = true)
interrupt_class = Class.new(RuntimeError)
ScratchPad.record []
in_handle_interrupt = Queue.new
can_continue = Queue.new
thread = Thread.new do
begin
Thread.handle_interrupt(interrupt_config) do
begin
in_handle_interrupt << true
if blocking
Thread.pass # Make it clearer the other thread needs to wait for this one to be in #pop
can_continue.pop
else
begin
can_continue.pop(true)
rescue ThreadError
Thread.pass
retry
end
end
rescue interrupt_class
ScratchPad << :interrupted
end
end
rescue interrupt_class
ScratchPad << :deferred
end
end
in_handle_interrupt.pop
if blocking
# Ensure the thread is inside Thread#pop, as if thread.raise is done before it would be deferred
Thread.pass until thread.stop?
end
thread.raise interrupt_class, "interrupt"
can_continue << true
thread.join
ScratchPad.recorded
end
before :each do
Thread.pending_interrupt?.should == false # sanity check
end
it "with :never defers interrupts until exiting the handle_interrupt block" do
make_handle_interrupt_thread(RuntimeError => :never).should == [:deferred]
end
it "with :on_blocking defers interrupts until the next blocking call" do
make_handle_interrupt_thread(RuntimeError => :on_blocking).should == [:interrupted]
make_handle_interrupt_thread({ RuntimeError => :on_blocking }, false).should == [:deferred]
end
it "with :immediate handles interrupts immediately" do
make_handle_interrupt_thread(RuntimeError => :immediate).should == [:interrupted]
end
it "with :immediate immediately runs pending interrupts, before the block" do
Thread.handle_interrupt(RuntimeError => :never) do
current = Thread.current
Thread.new {
current.raise "interrupt immediate"
}.join
Thread.pending_interrupt?.should == true
-> {
Thread.handle_interrupt(RuntimeError => :immediate) {
flunk "not reached"
}
}.should raise_error(RuntimeError, "interrupt immediate")
Thread.pending_interrupt?.should == false
end
end
it "also works with suspended Fibers and does not duplicate interrupts" do
fiber = Fiber.new { Fiber.yield }
fiber.resume
Thread.handle_interrupt(RuntimeError => :never) do
current = Thread.current
Thread.new {
current.raise "interrupt with fibers"
}.join
Thread.pending_interrupt?.should == true
-> {
Thread.handle_interrupt(RuntimeError => :immediate) {
flunk "not reached"
}
}.should raise_error(RuntimeError, "interrupt with fibers")
Thread.pending_interrupt?.should == false
end
fiber.resume
end
it "runs pending interrupts at the end of the block, even if there was an exception raised in the block" do
executed = false
-> {
Thread.handle_interrupt(RuntimeError => :never) do
current = Thread.current
Thread.new {
current.raise "interrupt exception"
}.join
Thread.pending_interrupt?.should == true
executed = true
raise "regular exception"
end
}.should raise_error(RuntimeError, "interrupt exception")
executed.should == true
end
it "supports multiple pairs in the Hash" do
make_handle_interrupt_thread(ArgumentError => :never, RuntimeError => :never).should == [:deferred]
end
end

View file

@ -0,0 +1,32 @@
require_relative '../../spec_helper'
describe "Thread.pending_interrupt?" do
it "returns false if there are no pending interrupts, e.g., outside any Thread.handle_interrupt block" do
Thread.pending_interrupt?.should == false
end
it "returns true if there are pending interrupts, e.g., Thread#raise inside Thread.handle_interrupt" do
executed = false
-> {
Thread.handle_interrupt(RuntimeError => :never) do
Thread.pending_interrupt?.should == false
current = Thread.current
Thread.new {
current.raise "interrupt"
}.join
Thread.pending_interrupt?.should == true
executed = true
end
}.should raise_error(RuntimeError, "interrupt")
executed.should == true
Thread.pending_interrupt?.should == false
end
end
describe "Thread#pending_interrupt?" do
it "returns whether the given threads has pending interrupts" do
Thread.current.pending_interrupt?.should == false
end
end

View file

@ -5,17 +5,31 @@ describe "Time#inspect" do
it_behaves_like :inspect, :inspect
ruby_version_is "2.7" do
it "preserves milliseconds" do
it "preserves microseconds" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456)
t.inspect.should == "2007-11-01 15:25:00.123456 UTC"
end
it "formats nanoseconds as a Rational" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789)
t.nsec.should == 123456789
t.strftime("%N").should == "123456789"
it "omits trailing zeros from microseconds" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 100000)
t.inspect.should == "2007-11-01 15:25:00.1 UTC"
end
t.inspect.should == "2007-11-01 15:25:00 8483885939586761/68719476736000000 UTC"
it "uses the correct time zone without microseconds" do
t = Time.utc(2000, 1, 1)
t = t.localtime(9*3600)
t.inspect.should == "2000-01-01 09:00:00 +0900"
end
it "uses the correct time zone with microseconds" do
t = Time.utc(2000, 1, 1, 0, 0, 0, 123456)
t = t.localtime(9*3600)
t.inspect.should == "2000-01-01 09:00:00.123456 +0900"
end
it "preserves nanoseconds" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
t.inspect.should == "2007-11-01 15:25:00.123456789 UTC"
end
end
end

View file

@ -551,11 +551,24 @@ describe "Module#private_constant marked constants" do
end
it "can be accessed from classes that include the module" do
ConstantVisibility::PrivConstModuleChild.new.private_constant_from_include.should be_true
ConstantVisibility::ClassIncludingPrivConstModule.new.private_constant_from_include.should be_true
end
it "can be accessed from modules that include the module" do
ConstantVisibility::ModuleIncludingPrivConstModule.private_constant_from_include.should be_true
end
it "raises a NameError when accessed directly from modules that include the module" do
-> do
ConstantVisibility::ModuleIncludingPrivConstModule.private_constant_self_from_include
end.should raise_error(NameError)
-> do
ConstantVisibility::ModuleIncludingPrivConstModule.private_constant_named_from_include
end.should raise_error(NameError)
end
it "is defined? from classes that include the module" do
ConstantVisibility::PrivConstModuleChild.new.defined_from_include.should == "constant"
ConstantVisibility::ClassIncludingPrivConstModule.new.defined_from_include.should == "constant"
end
end
@ -673,7 +686,7 @@ describe "Module#private_constant marked constants" do
}
-> do
ConstantVisibility::PrivConstModuleChild::PRIVATE_CONSTANT_MODULE
ConstantVisibility::ClassIncludingPrivConstModule::PRIVATE_CONSTANT_MODULE
end.should raise_error(NameError) {|e|
e.receiver.should == ConstantVisibility::PrivConstModule
e.name.should == :PRIVATE_CONSTANT_MODULE

View file

@ -65,7 +65,7 @@ module ConstantVisibility
end
end
class PrivConstModuleChild
class ClassIncludingPrivConstModule
include PrivConstModule
def private_constant_from_include
@ -77,6 +77,22 @@ module ConstantVisibility
end
end
module ModuleIncludingPrivConstModule
include PrivConstModule
def self.private_constant_from_include
PRIVATE_CONSTANT_MODULE
end
def self.private_constant_self_from_include
self::PRIVATE_CONSTANT_MODULE
end
def self.private_constant_named_from_include
PrivConstModule::PRIVATE_CONSTANT_MODULE
end
end
class PrivConstClassChild < PrivConstClass
def private_constant_from_subclass
PRIVATE_CONSTANT_CLASS

View file

@ -29,6 +29,14 @@ module SquigglyHeredocSpecs
HERE
end
def self.backslash
<<~HERE
a
b\
c
HERE
end
def self.least_indented_on_the_first_line
<<~HERE
a

View file

@ -100,6 +100,11 @@ HERE
SquigglyHeredocSpecs.singlequoted.should == "singlequoted \#{\"interpolated\"}\n"
end
it "allows HEREDOC with <<~'identifier', no interpolation, with backslash" do
require_relative 'fixtures/squiggly_heredoc'
SquigglyHeredocSpecs.backslash.should == "a\nbc\n"
end
it "selects the least-indented line and removes its indentation from all the lines" do
require_relative 'fixtures/squiggly_heredoc'
SquigglyHeredocSpecs.least_indented_on_the_first_line.should == "a\n b\n c\n"

View file

@ -0,0 +1,10 @@
require_relative '../../spec_helper'
require 'pathname'
describe "Pathname#inspect" do
it "returns a consistent String" do
result = Pathname.new('/tmp').inspect
result.should be_an_instance_of(String)
result.should == "#<Pathname:/tmp>"
end
end

View file

@ -99,6 +99,14 @@ describe 'BasicSocket#send' do
@server.close
end
describe 'with an object implementing #to_str' do
it 'returns the amount of sent bytes' do
data = mock('message')
data.should_receive(:to_str).and_return('hello')
@client.send(data, 0, @server.getsockname).should == 5
end
end
describe 'without a destination address' do
it "raises #{SocketSpecs.dest_addr_req_error}" do
-> { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)

View file

@ -27,6 +27,20 @@ describe "TCPSocket#recv_nonblock" do
@socket.recv_nonblock(50).should == "TCPSocket#recv_nonblock"
end
it 'writes the read to a buffer from the socket' do
@socket = TCPSocket.new @hostname, @server.port
@socket.write "TCPSocket#recv_nonblock"
# Wait for the server to echo. This spec is testing the return
# value, not the non-blocking behavior.
#
# TODO: Figure out a good way to test non-blocking.
IO.select([@socket])
buffer = "".b
@socket.recv_nonblock(50, 0, buffer)
buffer.should == 'TCPSocket#recv_nonblock'
end
it 'returns :wait_readable in exceptionless mode' do
@socket = TCPSocket.new @hostname, @server.port
@socket.recv_nonblock(50, exception: false).should == :wait_readable

View file

@ -51,6 +51,13 @@ describe 'UDPSocket#recvfrom_nonblock' do
@server.recvfrom_nonblock(1).should be_an_instance_of(Array)
end
it 'writes the data to the buffer when one is present' do
buffer = "".b
IO.select([@server])
@server.recvfrom_nonblock(1, 0, buffer)
buffer.should == 'h'
end
describe 'the returned Array' do
before do
IO.select([@server])

View file

@ -0,0 +1,68 @@
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 be_kind_of(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
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 be_an_instance_of(Binding)
b.local_variable_get(:a).should == "test"
end
ruby_version_is "2.6" do
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
"#{backtrace_location.path}:#{backtrace_location.lineno}".should == "#{binding.source_location[0]}:#{binding.source_location[1]}"
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 be_an_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 be_kind_of(Thread::Backtrace::Location) }
location = "#{__FILE__}:#{__LINE__ - 3}"
bts[1].to_s.should include(location)
end
end
end

View file

@ -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

View file

@ -462,6 +462,7 @@ void Init_object_spec(void) {
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, "not_implemented_method", rb_f_notimplement, 1);
}
#ifdef __cplusplus

View file

@ -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

View file

@ -134,6 +134,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

View file

@ -115,6 +115,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