mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Update to ruby/spec@8cafaa5
This commit is contained in:
parent
1b377b32c8
commit
2e32b919b4
35 changed files with 832 additions and 10 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
112
spec/ruby/core/module/ruby2_keywords_spec.rb
Normal file
112
spec/ruby/core/module/ruby2_keywords_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
64
spec/ruby/core/proc/ruby2_keywords_spec.rb
Normal file
64
spec/ruby/core/proc/ruby2_keywords_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 == "*"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
19
spec/ruby/core/symbol/name_spec.rb
Normal file
19
spec/ruby/core/symbol/name_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
125
spec/ruby/core/thread/handle_interrupt_spec.rb
Normal file
125
spec/ruby/core/thread/handle_interrupt_spec.rb
Normal 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
|
32
spec/ruby/core/thread/pending_interrupt_spec.rb
Normal file
32
spec/ruby/core/thread/pending_interrupt_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
10
spec/ruby/library/pathname/inspect_spec.rb
Normal file
10
spec/ruby/library/pathname/inspect_spec.rb
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
68
spec/ruby/optional/capi/debug_spec.rb
Normal file
68
spec/ruby/optional/capi/debug_spec.rb
Normal 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
|
93
spec/ruby/optional/capi/ext/debug_spec.c
Normal file
93
spec/ruby/optional/capi/ext/debug_spec.c
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue