mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Update to ruby/spec@37e52e5
This commit is contained in:
parent
dbea0be13d
commit
36dde35e02
48 changed files with 811 additions and 64 deletions
|
@ -1,4 +1,5 @@
|
||||||
require_relative '../../spec_helper'
|
require_relative '../../spec_helper'
|
||||||
|
require_relative 'fixtures/classes'
|
||||||
|
|
||||||
describe "Array#drop" do
|
describe "Array#drop" do
|
||||||
it "removes the specified number of elements from the start of the array" do
|
it "removes the specified number of elements from the start of the array" do
|
||||||
|
@ -48,4 +49,16 @@ describe "Array#drop" do
|
||||||
|
|
||||||
-> { [1, 2].drop(obj) }.should raise_error(TypeError)
|
-> { [1, 2].drop(obj) }.should raise_error(TypeError)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is ''...'3.0' do
|
||||||
|
it 'returns a subclass instance for Array subclasses' do
|
||||||
|
ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is '3.0' do
|
||||||
|
it 'returns a Array instance for Array subclasses' do
|
||||||
|
ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require_relative '../../spec_helper'
|
require_relative '../../spec_helper'
|
||||||
|
require_relative 'fixtures/classes'
|
||||||
|
|
||||||
describe "Array#drop_while" do
|
describe "Array#drop_while" do
|
||||||
it "removes elements from the start of the array while the block evaluates to true" do
|
it "removes elements from the start of the array while the block evaluates to true" do
|
||||||
|
@ -12,4 +13,16 @@ describe "Array#drop_while" do
|
||||||
it "removes elements from the start of the array until the block returns false" do
|
it "removes elements from the start of the array until the block returns false" do
|
||||||
[1, 2, 3, false, 5].drop_while { |n| n }.should == [false, 5]
|
[1, 2, 3, false, 5].drop_while { |n| n }.should == [false, 5]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is ''...'3.0' do
|
||||||
|
it 'returns a subclass instance for Array subclasses' do
|
||||||
|
ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is '3.0' do
|
||||||
|
it 'returns a Array instance for Array subclasses' do
|
||||||
|
ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -185,6 +185,64 @@ describe "Array#slice!" do
|
||||||
a.should == [3, 4]
|
a.should == [3, 4]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "with a subclass of Array" do
|
||||||
|
before :each do
|
||||||
|
@array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is ''...'3.0' do
|
||||||
|
it "returns a subclass instance with [n, m]" do
|
||||||
|
@array.slice!(0, 2).should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a subclass instance with [-n, m]" do
|
||||||
|
@array.slice!(-3, 2).should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a subclass instance with [n..m]" do
|
||||||
|
@array.slice!(1..3).should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a subclass instance with [n...m]" do
|
||||||
|
@array.slice!(1...3).should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a subclass instance with [-n..-m]" do
|
||||||
|
@array.slice!(-3..-1).should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a subclass instance with [-n...-m]" do
|
||||||
|
@array.slice!(-3...-1).should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is '3.0' do
|
||||||
|
it "returns a Array instance with [n, m]" do
|
||||||
|
@array.slice!(0, 2).should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a Array instance with [-n, m]" do
|
||||||
|
@array.slice!(-3, 2).should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a Array instance with [n..m]" do
|
||||||
|
@array.slice!(1..3).should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a Array instance with [n...m]" do
|
||||||
|
@array.slice!(1...3).should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a Array instance with [-n..-m]" do
|
||||||
|
@array.slice!(-3..-1).should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a Array instance with [-n...-m]" do
|
||||||
|
@array.slice!(-3...-1).should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Array#slice" do
|
describe "Array#slice" do
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require_relative '../../spec_helper'
|
require_relative '../../spec_helper'
|
||||||
|
require_relative 'fixtures/classes'
|
||||||
|
|
||||||
describe "Array#take" do
|
describe "Array#take" do
|
||||||
it "returns the first specified number of elements" do
|
it "returns the first specified number of elements" do
|
||||||
|
@ -24,4 +25,16 @@ describe "Array#take" do
|
||||||
it "raises an ArgumentError when the argument is negative" do
|
it "raises an ArgumentError when the argument is negative" do
|
||||||
->{ [1].take(-3) }.should raise_error(ArgumentError)
|
->{ [1].take(-3) }.should raise_error(ArgumentError)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is ''...'3.0' do
|
||||||
|
it 'returns a subclass instance for Array subclasses' do
|
||||||
|
ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is '3.0' do
|
||||||
|
it 'returns a Array instance for Array subclasses' do
|
||||||
|
ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require_relative '../../spec_helper'
|
require_relative '../../spec_helper'
|
||||||
|
require_relative 'fixtures/classes'
|
||||||
|
|
||||||
describe "Array#take_while" do
|
describe "Array#take_while" do
|
||||||
it "returns all elements until the block returns false" do
|
it "returns all elements until the block returns false" do
|
||||||
|
@ -12,4 +13,16 @@ describe "Array#take_while" do
|
||||||
it "returns all elements until the block returns false" do
|
it "returns all elements until the block returns false" do
|
||||||
[1, 2, false, 4].take_while{ |element| element }.should == [1, 2]
|
[1, 2, false, 4].take_while{ |element| element }.should == [1, 2]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is ''...'3.0' do
|
||||||
|
it 'returns a subclass instance for Array subclasses' do
|
||||||
|
ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should be_an_instance_of(ArraySpecs::MyArray)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is '3.0' do
|
||||||
|
it 'returns a Array instance for Array subclasses' do
|
||||||
|
ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should be_an_instance_of(Array)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,4 +83,63 @@ describe "BasicObject#singleton_method_added" do
|
||||||
end
|
end
|
||||||
ScratchPad.recorded.should == [:singleton_method_added, :new_method_with_define_method]
|
ScratchPad.recorded.should == [:singleton_method_added, :new_method_with_define_method]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "when singleton_method_added is undefined" do
|
||||||
|
it "raises NoMethodError for a metaclass" do
|
||||||
|
class BasicObjectSpecs::NoSingletonMethodAdded
|
||||||
|
class << self
|
||||||
|
undef_method :singleton_method_added
|
||||||
|
end
|
||||||
|
|
||||||
|
-> {
|
||||||
|
def self.foo
|
||||||
|
end
|
||||||
|
}.should raise_error(NoMethodError, /undefined method `singleton_method_added' for/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises NoMethodError for a singleton instance" do
|
||||||
|
object = Object.new
|
||||||
|
class << object
|
||||||
|
undef_method :singleton_method_added
|
||||||
|
|
||||||
|
-> {
|
||||||
|
def foo
|
||||||
|
end
|
||||||
|
}.should raise_error(NoMethodError, /undefined method `singleton_method_added' for #<Object:/)
|
||||||
|
|
||||||
|
-> {
|
||||||
|
define_method(:bar) {}
|
||||||
|
}.should raise_error(NoMethodError, /undefined method `singleton_method_added' for #<Object:/)
|
||||||
|
end
|
||||||
|
|
||||||
|
-> {
|
||||||
|
object.define_singleton_method(:baz) {}
|
||||||
|
}.should raise_error(NoMethodError, /undefined method `singleton_method_added' for #<Object:/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls #method_missing" do
|
||||||
|
ScratchPad.record []
|
||||||
|
object = Object.new
|
||||||
|
class << object
|
||||||
|
def method_missing(*args)
|
||||||
|
ScratchPad << args
|
||||||
|
end
|
||||||
|
|
||||||
|
undef_method :singleton_method_added
|
||||||
|
|
||||||
|
def foo
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method(:bar) {}
|
||||||
|
end
|
||||||
|
object.define_singleton_method(:baz) {}
|
||||||
|
|
||||||
|
ScratchPad.recorded.should == [
|
||||||
|
[:singleton_method_added, :foo],
|
||||||
|
[:singleton_method_added, :bar],
|
||||||
|
[:singleton_method_added, :baz],
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -100,10 +100,18 @@ ruby_version_is "2.6" do
|
||||||
a.should == %w|.dotfile.ext directory|
|
a.should == %w|.dotfile.ext directory|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "accepts an options Hash" do
|
it "accepts an encoding keyword for the encoding of the entries" do
|
||||||
@dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8")
|
@dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8")
|
||||||
a = @dir.children.sort
|
dirs = @dir.to_a.sort
|
||||||
a.should == %w|.dotfile.ext directory|
|
dirs.each { |d| d.encoding.should == Encoding::UTF_8 }
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is ""..."2.7" do
|
||||||
|
it "accepts nil options" do
|
||||||
|
@dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", nil)
|
||||||
|
dirs = @dir.to_a.sort
|
||||||
|
dirs.each { |d| d.encoding.should == Encoding.find("filesystem") }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns children encoded with the filesystem encoding by default" do
|
it "returns children encoded with the filesystem encoding by default" do
|
||||||
|
|
|
@ -10,6 +10,18 @@ describe "Dir.each_child" do
|
||||||
DirSpecs.delete_mock_dirs
|
DirSpecs.delete_mock_dirs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "accepts an encoding keyword for the encoding of the entries" do
|
||||||
|
dirs = Dir.each_child("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort
|
||||||
|
dirs.each {|dir| dir.encoding.should == Encoding::UTF_8}
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is ""..."2.7" do
|
||||||
|
it "accepts nil options" do
|
||||||
|
dirs = Dir.each_child("#{DirSpecs.mock_dir}/deeply/nested", nil).to_a.sort
|
||||||
|
dirs.each {|dir| dir.encoding.should == Encoding.find("filesystem")}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "yields all names in an existing directory to the provided block" do
|
it "yields all names in an existing directory to the provided block" do
|
||||||
a, b = [], []
|
a, b = [], []
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,16 @@ describe "Dir.entries" do
|
||||||
Dir.entries(p)
|
Dir.entries(p)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "accepts an options Hash" do
|
it "accepts an encoding keyword for the encoding of the entries" do
|
||||||
a = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort
|
dirs = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort
|
||||||
a.should == %w|. .. .dotfile.ext directory|
|
dirs.each {|dir| dir.encoding.should == Encoding::UTF_8}
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is ""..."2.7" do
|
||||||
|
it "accepts nil options" do
|
||||||
|
dirs = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", nil).to_a.sort
|
||||||
|
dirs.each {|dir| dir.encoding.should == Encoding.find("filesystem")}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns entries encoded with the filesystem encoding by default" do
|
it "returns entries encoded with the filesystem encoding by default" do
|
||||||
|
|
|
@ -39,6 +39,18 @@ describe "Dir.foreach" do
|
||||||
Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths
|
Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "accepts an encoding keyword for the encoding of the entries" do
|
||||||
|
dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort
|
||||||
|
dirs.each {|dir| dir.encoding.should == Encoding::UTF_8}
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is ""..."2.7" do
|
||||||
|
it "accepts nil options" do
|
||||||
|
dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", nil).to_a.sort
|
||||||
|
dirs.each {|dir| dir.encoding.should == Encoding.find("filesystem")}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "when no block is given" do
|
describe "when no block is given" do
|
||||||
it "returns an Enumerator" do
|
it "returns an Enumerator" do
|
||||||
Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator)
|
Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator)
|
||||||
|
|
|
@ -6,4 +6,9 @@ describe "Enumerator::Lazy#chunk_while" do
|
||||||
s.lazy.chunk_while { |a, b| false }.first(100).should ==
|
s.lazy.chunk_while { |a, b| false }.first(100).should ==
|
||||||
s.first(100).chunk_while { |a, b| false }.to_a
|
s.first(100).chunk_while { |a, b| false }.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should return a lazy enumerator" do
|
||||||
|
s = 0..Float::INFINITY
|
||||||
|
s.lazy.chunk_while { |a, b| false }.should be_kind_of(Enumerator::Lazy)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,4 +6,9 @@ describe "Enumerator::Lazy#slice_after" do
|
||||||
s.lazy.slice_after { |n| true }.first(100).should ==
|
s.lazy.slice_after { |n| true }.first(100).should ==
|
||||||
s.first(100).slice_after { |n| true }.to_a
|
s.first(100).slice_after { |n| true }.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should return a lazy enumerator" do
|
||||||
|
s = 0..Float::INFINITY
|
||||||
|
s.lazy.slice_after { |n| true }.should be_kind_of(Enumerator::Lazy)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,4 +6,9 @@ describe "Enumerator::Lazy#slice_before" do
|
||||||
s.lazy.slice_before { |n| true }.first(100).should ==
|
s.lazy.slice_before { |n| true }.first(100).should ==
|
||||||
s.first(100).slice_before { |n| true }.to_a
|
s.first(100).slice_before { |n| true }.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should return a lazy enumerator" do
|
||||||
|
s = 0..Float::INFINITY
|
||||||
|
s.lazy.slice_before { |n| true }.should be_kind_of(Enumerator::Lazy)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,4 +6,9 @@ describe "Enumerator::Lazy#slice_when" do
|
||||||
s.lazy.slice_when { |a, b| true }.first(100).should ==
|
s.lazy.slice_when { |a, b| true }.first(100).should ==
|
||||||
s.first(100).slice_when { |a, b| true }.to_a
|
s.first(100).slice_when { |a, b| true }.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should return a lazy enumerator" do
|
||||||
|
s = 0..Float::INFINITY
|
||||||
|
s.lazy.slice_when { |a, b| true }.should be_kind_of(Enumerator::Lazy)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
36
spec/ruby/core/env/except_spec.rb
vendored
Normal file
36
spec/ruby/core/env/except_spec.rb
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
require_relative 'spec_helper'
|
||||||
|
require_relative 'shared/to_hash'
|
||||||
|
|
||||||
|
ruby_version_is "3.0" do
|
||||||
|
describe "ENV.except" do
|
||||||
|
before do
|
||||||
|
@orig_hash = ENV.to_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
ENV.replace @orig_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
# Testing the method without arguments is covered via
|
||||||
|
it_behaves_like :env_to_hash, :except
|
||||||
|
|
||||||
|
it "returns a hash without the requested subset" do
|
||||||
|
ENV.clear
|
||||||
|
|
||||||
|
ENV['one'] = '1'
|
||||||
|
ENV['two'] = '2'
|
||||||
|
ENV['three'] = '3'
|
||||||
|
|
||||||
|
ENV.except('one', 'three').should == { 'two' => '2' }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ignores keys not present in the original hash" do
|
||||||
|
ENV.clear
|
||||||
|
|
||||||
|
ENV['one'] = '1'
|
||||||
|
ENV['two'] = '2'
|
||||||
|
|
||||||
|
ENV.except('one', 'three').should == { 'two' => '2' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -45,19 +45,36 @@ describe "IO#close" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an IOError with a clear message' do
|
it 'raises an IOError with a clear message' do
|
||||||
read_io, write_io = IO.pipe
|
matching_exception = nil
|
||||||
going_to_read = false
|
|
||||||
thread = Thread.new do
|
|
||||||
-> do
|
|
||||||
going_to_read = true
|
|
||||||
read_io.read
|
|
||||||
end.should raise_error(IOError, 'stream closed in another thread')
|
|
||||||
end
|
|
||||||
|
|
||||||
Thread.pass until going_to_read && thread.stop?
|
-> do
|
||||||
read_io.close
|
IOSpecs::THREAD_CLOSE_RETRIES.times do
|
||||||
thread.join
|
read_io, write_io = IO.pipe
|
||||||
write_io.close
|
going_to_read = false
|
||||||
|
|
||||||
|
thread = Thread.new do
|
||||||
|
begin
|
||||||
|
going_to_read = true
|
||||||
|
read_io.read
|
||||||
|
rescue IOError => ioe
|
||||||
|
if ioe.message == IOSpecs::THREAD_CLOSE_ERROR_MESSAGE
|
||||||
|
matching_exception = ioe
|
||||||
|
end
|
||||||
|
# try again
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# best attempt to ensure the thread is actually blocked on read
|
||||||
|
Thread.pass until going_to_read && thread.stop?
|
||||||
|
sleep(0.001)
|
||||||
|
|
||||||
|
read_io.close
|
||||||
|
thread.join
|
||||||
|
write_io.close
|
||||||
|
|
||||||
|
matching_exception&.tap {|ex| raise ex}
|
||||||
|
end
|
||||||
|
end.should raise_error(IOError, IOSpecs::THREAD_CLOSE_ERROR_MESSAGE)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
module IOSpecs
|
module IOSpecs
|
||||||
|
THREAD_CLOSE_RETRIES = 10
|
||||||
|
THREAD_CLOSE_ERROR_MESSAGE = 'stream closed in another thread'
|
||||||
|
|
||||||
class SubIO < IO
|
class SubIO < IO
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -362,18 +362,19 @@ module KernelSpecs
|
||||||
class RespondViaMissing
|
class RespondViaMissing
|
||||||
def respond_to_missing?(method, priv=false)
|
def respond_to_missing?(method, priv=false)
|
||||||
case method
|
case method
|
||||||
when :handled_publicly
|
when :handled_publicly
|
||||||
true
|
true
|
||||||
when :handled_privately
|
when :handled_privately
|
||||||
priv
|
priv
|
||||||
when :not_handled
|
when :not_handled
|
||||||
false
|
false
|
||||||
else
|
else
|
||||||
raise "Typo in method name"
|
raise "Typo in method name: #{method.inspect}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def method_missing(method, *args)
|
def method_missing(method, *args)
|
||||||
|
raise "the method name should be a Symbol" unless Symbol === method
|
||||||
"Done #{method}(#{args})"
|
"Done #{method}(#{args})"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -104,5 +104,13 @@ describe "Kernel#public_send" do
|
||||||
}.should raise_error(NoMethodError)
|
}.should raise_error(NoMethodError)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "includes `public_send` in the backtrace when passed not enough arguments" do
|
||||||
|
-> { public_send() }.should raise_error(ArgumentError) { |e| e.backtrace[0].should.include?("`public_send'") }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "includes `public_send` in the backtrace when passed a single incorrect argument" do
|
||||||
|
-> { public_send(Object.new) }.should raise_error(TypeError) { |e| e.backtrace[0].should.include?("`public_send'") }
|
||||||
|
end
|
||||||
|
|
||||||
it_behaves_like :basicobject_send, :public_send
|
it_behaves_like :basicobject_send, :public_send
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,12 +15,18 @@ describe :kernel_method, shared: true do
|
||||||
m.call.should == 'class done'
|
m.call.should == 'class done'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns a method object if we repond_to_missing? method" do
|
it "returns a method object if respond_to_missing?(method) is true" do
|
||||||
m = KernelSpecs::RespondViaMissing.new.send(@method, :handled_publicly)
|
m = KernelSpecs::RespondViaMissing.new.send(@method, :handled_publicly)
|
||||||
m.should be_an_instance_of Method
|
m.should be_an_instance_of Method
|
||||||
m.call(42).should == "Done handled_publicly([42])"
|
m.call(42).should == "Done handled_publicly([42])"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "the returned method object if respond_to_missing?(method) calls #method_missing with a Symbol name" do
|
||||||
|
m = KernelSpecs::RespondViaMissing.new.send(@method, "handled_publicly")
|
||||||
|
m.should be_an_instance_of Method
|
||||||
|
m.call(42).should == "Done handled_publicly([42])"
|
||||||
|
end
|
||||||
|
|
||||||
it "raises a NameError for an invalid method name" do
|
it "raises a NameError for an invalid method name" do
|
||||||
class KernelSpecs::Foo; def bar; 'done'; end; end
|
class KernelSpecs::Foo; def bar; 'done'; end; end
|
||||||
-> {
|
-> {
|
||||||
|
|
|
@ -546,8 +546,12 @@ describe :kernel_require, shared: true do
|
||||||
ScratchPad.recorded.should == []
|
ScratchPad.recorded.should == []
|
||||||
end
|
end
|
||||||
|
|
||||||
it "complex, enumerator, rational and thread are already required" do
|
provided = %w[complex enumerator rational thread]
|
||||||
provided = %w[complex enumerator rational thread]
|
ruby_version_is "2.7" do
|
||||||
|
provided << 'ruby2_keywords'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "#{provided.join(', ')} are already required" do
|
||||||
features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
|
features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
|
||||||
provided.each { |feature|
|
provided.each { |feature|
|
||||||
features.should =~ /\b#{feature}\.(rb|so|jar)$/
|
features.should =~ /\b#{feature}\.(rb|so|jar)$/
|
||||||
|
|
11
spec/ruby/core/main/ruby2_keywords_spec.rb
Normal file
11
spec/ruby/core/main/ruby2_keywords_spec.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
require_relative '../../spec_helper'
|
||||||
|
require_relative 'fixtures/classes'
|
||||||
|
|
||||||
|
ruby_version_is "2.7" do
|
||||||
|
describe "main.ruby2_keywords" do
|
||||||
|
it "is the same as Object.ruby2_keywords" do
|
||||||
|
main = TOPLEVEL_BINDING.receiver
|
||||||
|
main.should have_private_method(:ruby2_keywords)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -33,7 +33,20 @@ describe "Module#attr_accessor" do
|
||||||
attr_accessor :spec_attr_accessor
|
attr_accessor :spec_attr_accessor
|
||||||
end
|
end
|
||||||
|
|
||||||
-> { true.spec_attr_accessor = "a" }.should raise_error(RuntimeError)
|
-> { true.spec_attr_accessor = "a" }.should raise_error(FrozenError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises FrozenError if the receiver if frozen" do
|
||||||
|
c = Class.new do
|
||||||
|
attr_accessor :foo
|
||||||
|
end
|
||||||
|
obj = c.new
|
||||||
|
obj.foo = 1
|
||||||
|
obj.foo.should == 1
|
||||||
|
|
||||||
|
obj.freeze
|
||||||
|
-> { obj.foo = 42 }.should raise_error(FrozenError)
|
||||||
|
obj.foo.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it "converts non string/symbol names to strings using to_str" do
|
it "converts non string/symbol names to strings using to_str" do
|
||||||
|
|
|
@ -29,7 +29,17 @@ describe "Module#attr_writer" do
|
||||||
attr_writer :spec_attr_writer
|
attr_writer :spec_attr_writer
|
||||||
end
|
end
|
||||||
|
|
||||||
-> { true.spec_attr_writer = "a" }.should raise_error(RuntimeError)
|
-> { true.spec_attr_writer = "a" }.should raise_error(FrozenError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises FrozenError if the receiver if frozen" do
|
||||||
|
c = Class.new do
|
||||||
|
attr_writer :foo
|
||||||
|
end
|
||||||
|
obj = c.new
|
||||||
|
obj.freeze
|
||||||
|
|
||||||
|
-> { obj.foo = 42 }.should raise_error(FrozenError)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "converts non string/symbol names to strings using to_str" do
|
it "converts non string/symbol names to strings using to_str" do
|
||||||
|
|
|
@ -40,6 +40,11 @@ describe "Module#const_defined?" do
|
||||||
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, true).should be_true
|
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, true).should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "coerces the inherit flag to a boolean" do
|
||||||
|
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, nil).should be_false
|
||||||
|
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, :true).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
it "returns true if the given String names a constant defined in the receiver" do
|
it "returns true if the given String names a constant defined in the receiver" do
|
||||||
ConstantSpecs.const_defined?("CS_CONST2").should == true
|
ConstantSpecs.const_defined?("CS_CONST2").should == true
|
||||||
ConstantSpecs.const_defined?("ModuleA").should == true
|
ConstantSpecs.const_defined?("ModuleA").should == true
|
||||||
|
|
|
@ -88,6 +88,14 @@ describe "Module#const_get" do
|
||||||
end.should raise_error(NameError)
|
end.should raise_error(NameError)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "coerces the inherit flag to a boolean" do
|
||||||
|
ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST4, :true).should == :const4
|
||||||
|
|
||||||
|
-> do
|
||||||
|
ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST1, nil)
|
||||||
|
end.should raise_error(NameError)
|
||||||
|
end
|
||||||
|
|
||||||
it "accepts a toplevel scope qualifier" do
|
it "accepts a toplevel scope qualifier" do
|
||||||
ConstantSpecs.const_get("::CS_CONST1").should == :const1
|
ConstantSpecs.const_get("::CS_CONST1").should == :const1
|
||||||
end
|
end
|
||||||
|
|
|
@ -573,6 +573,43 @@ describe "Module#refine" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "" ... "2.7" do
|
||||||
|
it "is not honored by Kernel#public_method" do
|
||||||
|
klass = Class.new
|
||||||
|
refinement = Module.new do
|
||||||
|
refine klass do
|
||||||
|
def foo; end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-> {
|
||||||
|
Module.new do
|
||||||
|
using refinement
|
||||||
|
klass.new.public_method(:foo)
|
||||||
|
end
|
||||||
|
}.should raise_error(NameError, /undefined method `foo'/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is "2.7" do
|
||||||
|
it "is honored by Kernel#public_method" do
|
||||||
|
klass = Class.new
|
||||||
|
refinement = Module.new do
|
||||||
|
refine klass do
|
||||||
|
def foo; end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result = nil
|
||||||
|
Module.new do
|
||||||
|
using refinement
|
||||||
|
result = klass.new.public_method(:foo).class
|
||||||
|
end
|
||||||
|
|
||||||
|
result.should == Method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
ruby_version_is "" ... "2.7" do
|
ruby_version_is "" ... "2.7" do
|
||||||
it "is not honored by Kernel#instance_method" do
|
it "is not honored by Kernel#instance_method" do
|
||||||
klass = Class.new
|
klass = Class.new
|
||||||
|
@ -592,7 +629,7 @@ describe "Module#refine" do
|
||||||
end
|
end
|
||||||
|
|
||||||
ruby_version_is "2.7" do
|
ruby_version_is "2.7" do
|
||||||
it "is honored by Kernel#method" do
|
it "is honored by Kernel#instance_method" do
|
||||||
klass = Class.new
|
klass = Class.new
|
||||||
refinement = Module.new do
|
refinement = Module.new do
|
||||||
refine klass do
|
refine klass do
|
||||||
|
|
|
@ -49,6 +49,9 @@ describe :proc_call_on_proc_or_lambda, shared: true do
|
||||||
|
|
||||||
a = proc {|x| x}.send(@method, 1, 2, 3)
|
a = proc {|x| x}.send(@method, 1, 2, 3)
|
||||||
a.should == 1
|
a.should == 1
|
||||||
|
|
||||||
|
a = proc {|x:| x}.send(@method, 2, x: 1)
|
||||||
|
a.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it "will call #to_ary on argument and return self if return is nil" do
|
it "will call #to_ary on argument and return self if return is nil" do
|
||||||
|
|
|
@ -22,4 +22,14 @@ describe "Regexp#===" do
|
||||||
(/abc/ === nil).should be_false
|
(/abc/ === nil).should be_false
|
||||||
(/abc/ === /abc/).should be_false
|
(/abc/ === /abc/).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "uses #to_str on string-like objects" do
|
||||||
|
stringlike = Class.new do
|
||||||
|
def to_str
|
||||||
|
"abc"
|
||||||
|
end
|
||||||
|
end.new
|
||||||
|
|
||||||
|
(/abc/ === stringlike).should be_true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,7 +49,21 @@ platform_is_not :windows do
|
||||||
|
|
||||||
it "registers an handler doing nothing with :IGNORE" do
|
it "registers an handler doing nothing with :IGNORE" do
|
||||||
Signal.trap :HUP, :IGNORE
|
Signal.trap :HUP, :IGNORE
|
||||||
|
Signal.trap(:HUP, @saved_trap).should == "IGNORE"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can register a new handler after :IGNORE" do
|
||||||
|
Signal.trap :HUP, :IGNORE
|
||||||
|
|
||||||
|
done = false
|
||||||
|
Signal.trap(:HUP) do
|
||||||
|
ScratchPad.record :block_trap
|
||||||
|
done = true
|
||||||
|
end
|
||||||
|
|
||||||
Process.kill(:HUP, Process.pid).should == 1
|
Process.kill(:HUP, Process.pid).should == 1
|
||||||
|
Thread.pass until done
|
||||||
|
ScratchPad.recorded.should == :block_trap
|
||||||
end
|
end
|
||||||
|
|
||||||
it "ignores the signal when passed nil" do
|
it "ignores the signal when passed nil" do
|
||||||
|
|
|
@ -105,4 +105,18 @@ describe "String#scrub!" do
|
||||||
input.scrub! { |b| "<?>" }
|
input.scrub! { |b| "<?>" }
|
||||||
input.should == "a<?>"
|
input.should == "a<?>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "maintains the state of frozen strings that are already valid" do
|
||||||
|
input = "a"
|
||||||
|
input.freeze
|
||||||
|
input.scrub!
|
||||||
|
input.frozen?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "preserves the instance variables of already valid strings" do
|
||||||
|
input = "a"
|
||||||
|
input.instance_variable_set(:@a, 'b')
|
||||||
|
input.scrub!
|
||||||
|
input.instance_variable_get(:@a).should == 'b'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,15 +19,15 @@ describe :time_now, shared: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "has at least microsecond precision" do
|
it "has at least microsecond precision" do
|
||||||
times = []
|
|
||||||
10_000.times do
|
|
||||||
times << Time.now.nsec
|
|
||||||
end
|
|
||||||
|
|
||||||
# The clock should not be less accurate than expected (times should
|
# The clock should not be less accurate than expected (times should
|
||||||
# not all be a multiple of the next precision up, assuming precisions
|
# not all be a multiple of the next precision up, assuming precisions
|
||||||
# are multiples of ten.)
|
# are multiples of ten.)
|
||||||
expected = 1_000
|
expected = 1_000
|
||||||
times.select { |t| t % (expected * 10) == 0 }.size.should_not == times.size
|
t = 0
|
||||||
|
10_000.times.find do
|
||||||
|
t = Time.now.nsec
|
||||||
|
t % (expected * 10) != 0
|
||||||
|
end
|
||||||
|
(t % (expected * 10)).should != 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@ ScratchPad.recorded << :con_pre
|
||||||
Thread.current[:in_concurrent_rb] = true
|
Thread.current[:in_concurrent_rb] = true
|
||||||
|
|
||||||
if t = Thread.current[:wait_for]
|
if t = Thread.current[:wait_for]
|
||||||
Thread.pass until t.backtrace && t.backtrace.any? { |call| call.include? 'require' }
|
Thread.pass until t.backtrace && t.backtrace.any? { |call| call.include? 'require' } && t.stop?
|
||||||
end
|
end
|
||||||
|
|
||||||
if Thread.current[:con_raise]
|
if Thread.current[:con_raise]
|
||||||
|
|
|
@ -63,6 +63,11 @@ describe "Regexps with back-references" do
|
||||||
(/\10()()()()()()()()()()/ =~ "\x08").should == 0
|
(/\10()()()()()()()()()()/ =~ "\x08").should == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "fails when trying to match a backreference to an unmatched capture group" do
|
||||||
|
/\1()/.match("").should == nil
|
||||||
|
/(?:(a)|b)\1/.match("b").should == nil
|
||||||
|
end
|
||||||
|
|
||||||
it "ignores backreferences > 1000" do
|
it "ignores backreferences > 1000" do
|
||||||
/\99999/.match("99999")[0].should == "99999"
|
/\99999/.match("99999")[0].should == "99999"
|
||||||
end
|
end
|
||||||
|
|
135
spec/ruby/language/regexp/empty_checks_spec.rb
Normal file
135
spec/ruby/language/regexp/empty_checks_spec.rb
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
require_relative '../../spec_helper'
|
||||||
|
require_relative '../fixtures/classes'
|
||||||
|
|
||||||
|
describe "empty checks in Regexps" do
|
||||||
|
|
||||||
|
it "allow extra empty iterations" do
|
||||||
|
/()?/.match("").to_a.should == ["", ""]
|
||||||
|
/(a*)?/.match("").to_a.should == ["", ""]
|
||||||
|
/(a*)*/.match("").to_a.should == ["", ""]
|
||||||
|
# The bounds are high to avoid DFA-based matchers in implementations
|
||||||
|
# and to check backtracking behavior.
|
||||||
|
/(?:a|()){500,1000}/.match("a" * 500).to_a.should == ["a" * 500, ""]
|
||||||
|
|
||||||
|
# Variations with non-greedy loops.
|
||||||
|
/()??/.match("").to_a.should == ["", nil]
|
||||||
|
/(a*?)?/.match("").to_a.should == ["", ""]
|
||||||
|
/(a*)??/.match("").to_a.should == ["", nil]
|
||||||
|
/(a*?)??/.match("").to_a.should == ["", nil]
|
||||||
|
/(a*?)*/.match("").to_a.should == ["", ""]
|
||||||
|
/(a*)*?/.match("").to_a.should == ["", nil]
|
||||||
|
/(a*?)*?/.match("").to_a.should == ["", nil]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allow empty iterations in the middle of a loop" do
|
||||||
|
# One empty iteration between a's and b's.
|
||||||
|
/(a|\2b|())*/.match("aaabbb").to_a.should == ["aaabbb", "", ""]
|
||||||
|
/(a|\2b|()){2,4}/.match("aaabbb").to_a.should == ["aaa", "", ""]
|
||||||
|
|
||||||
|
# Two empty iterations between a's and b's.
|
||||||
|
/(a|\2b|\3()|())*/.match("aaabbb").to_a.should == ["aaabbb", "", "", ""]
|
||||||
|
/(a|\2b|\3()|()){2,4}/.match("aaabbb").to_a.should == ["aaa", "", nil, ""]
|
||||||
|
|
||||||
|
# Check that the empty iteration correctly updates the loop counter.
|
||||||
|
/(a|\2b|()){20,24}/.match("a" * 20 + "b" * 5).to_a.should == ["a" * 20 + "b" * 3, "b", ""]
|
||||||
|
|
||||||
|
# Variations with non-greedy loops.
|
||||||
|
/(a|\2b|())*?/.match("aaabbb").to_a.should == ["", nil, nil]
|
||||||
|
/(a|\2b|()){2,4}/.match("aaabbb").to_a.should == ["aaa", "", ""]
|
||||||
|
/(a|\2b|\3()|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil]
|
||||||
|
/(a|\2b|\3()|()){2,4}/.match("aaabbb").to_a.should == ["aaa", "", nil, ""]
|
||||||
|
/(a|\2b|()){20,24}/.match("a" * 20 + "b" * 5).to_a.should == ["a" * 20 + "b" * 3, "b", ""]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "make the Regexp proceed past the quantified expression on failure" do
|
||||||
|
# If the contents of the ()* quantified group are empty (i.e., they fail
|
||||||
|
# the empty check), the loop will abort. It will not try to backtrack
|
||||||
|
# and try other alternatives (e.g. matching the "a") like in other Regexp
|
||||||
|
# dialects such as ECMAScript.
|
||||||
|
/(?:|a)*/.match("aaa").to_a.should == [""]
|
||||||
|
/(?:()|a)*/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/(|a)*/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/(()|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
|
||||||
|
# Same expressions, but with backreferences, to force the use of non-DFA-based
|
||||||
|
# engines.
|
||||||
|
/()\1(?:|a)*/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/()\1(?:()|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
/()\1(|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
/()\1(()|a)*/.match("aaa").to_a.should == ["", "", "", ""]
|
||||||
|
|
||||||
|
# Variations with other zero-width contents of the quantified
|
||||||
|
# group: backreferences, capture groups, lookarounds
|
||||||
|
/()(?:\1|a)*/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/()(?:()\1|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
/()(?:(\1)|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
/()(?:\1()|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
/()(\1|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
/()(()\1|a)*/.match("aaa").to_a.should == ["", "", "", ""]
|
||||||
|
/()((\1)|a)*/.match("aaa").to_a.should == ["", "", "", ""]
|
||||||
|
/()(\1()|a)*/.match("aaa").to_a.should == ["", "", "", ""]
|
||||||
|
|
||||||
|
/(?:(?=a)|a)*/.match("aaa").to_a.should == [""]
|
||||||
|
/(?:(?=a)()|a)*/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/(?:()(?=a)|a)*/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/(?:((?=a))|a)*/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/()\1(?:(?=a)|a)*/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/()\1(?:(?=a)()|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
/()\1(?:()(?=a)|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
/()\1(?:((?=a))|a)*/.match("aaa").to_a.should == ["", "", ""]
|
||||||
|
|
||||||
|
# Variations with non-greedy loops.
|
||||||
|
/(?:|a)*?/.match("aaa").to_a.should == [""]
|
||||||
|
/(?:()|a)*?/.match("aaa").to_a.should == ["", nil]
|
||||||
|
/(|a)*?/.match("aaa").to_a.should == ["", nil]
|
||||||
|
/(()|a)*?/.match("aaa").to_a.should == ["", nil, nil]
|
||||||
|
|
||||||
|
/()\1(?:|a)*?/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/()\1(?:()|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
/()\1(|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
/()\1(()|a)*?/.match("aaa").to_a.should == ["", "", nil, nil]
|
||||||
|
|
||||||
|
/()(?:\1|a)*?/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/()(?:()\1|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
/()(?:(\1)|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
/()(?:\1()|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
/()(\1|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
/()(()\1|a)*?/.match("aaa").to_a.should == ["", "", nil, nil]
|
||||||
|
/()((\1)|a)*?/.match("aaa").to_a.should == ["", "", nil, nil]
|
||||||
|
/()(\1()|a)*?/.match("aaa").to_a.should == ["", "", nil, nil]
|
||||||
|
|
||||||
|
/(?:(?=a)|a)*?/.match("aaa").to_a.should == [""]
|
||||||
|
/(?:(?=a)()|a)*?/.match("aaa").to_a.should == ["", nil]
|
||||||
|
/(?:()(?=a)|a)*?/.match("aaa").to_a.should == ["", nil]
|
||||||
|
/(?:((?=a))|a)*?/.match("aaa").to_a.should == ["", nil]
|
||||||
|
/()\1(?:(?=a)|a)*?/.match("aaa").to_a.should == ["", ""]
|
||||||
|
/()\1(?:(?=a)()|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
/()\1(?:()(?=a)|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
/()\1(?:((?=a))|a)*?/.match("aaa").to_a.should == ["", "", nil]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "shouldn't cause the Regexp parser to get stuck in a loop" do
|
||||||
|
/(|a|\2b|())*/.match("aaabbb").to_a.should == ["", "", nil]
|
||||||
|
/(a||\2b|())*/.match("aaabbb").to_a.should == ["aaa", "", nil]
|
||||||
|
/(a|\2b||())*/.match("aaabbb").to_a.should == ["aaa", "", nil]
|
||||||
|
/(a|\2b|()|)*/.match("aaabbb").to_a.should == ["aaabbb", "", ""]
|
||||||
|
/(()|a|\3b|())*/.match("aaabbb").to_a.should == ["", "", "", nil]
|
||||||
|
/(a|()|\3b|())*/.match("aaabbb").to_a.should == ["aaa", "", "", nil]
|
||||||
|
/(a|\2b|()|())*/.match("aaabbb").to_a.should == ["aaabbb", "", "", nil]
|
||||||
|
/(a|\3b|()|())*/.match("aaabbb").to_a.should == ["aaa", "", "", nil]
|
||||||
|
/(a|()|())*/.match("aaa").to_a.should == ["aaa", "", "", nil]
|
||||||
|
/^(()|a|())*$/.match("aaa").to_a.should == ["aaa", "", "", nil]
|
||||||
|
|
||||||
|
# Variations with non-greedy loops.
|
||||||
|
/(|a|\2b|())*?/.match("aaabbb").to_a.should == ["", nil, nil]
|
||||||
|
/(a||\2b|())*?/.match("aaabbb").to_a.should == ["", nil, nil]
|
||||||
|
/(a|\2b||())*?/.match("aaabbb").to_a.should == ["", nil, nil]
|
||||||
|
/(a|\2b|()|)*?/.match("aaabbb").to_a.should == ["", nil, nil]
|
||||||
|
/(()|a|\3b|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil]
|
||||||
|
/(a|()|\3b|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil]
|
||||||
|
/(a|\2b|()|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil]
|
||||||
|
/(a|\3b|()|())*?/.match("aaabbb").to_a.should == ["", nil, nil, nil]
|
||||||
|
/(a|()|())*?/.match("aaa").to_a.should == ["", nil, nil, nil]
|
||||||
|
/^(()|a|())*?$/.match("aaa").to_a.should == ["aaa", "a", "", nil]
|
||||||
|
end
|
||||||
|
end
|
|
@ -128,4 +128,15 @@ describe "Regexps with repetition" do
|
||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "treats ? after {n} quantifier as another quantifier, not as non-greedy marker" do
|
||||||
|
/a{2}?/.match("").to_a.should == [""]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches zero-width capture groups in optional iterations of loops" do
|
||||||
|
/()?/.match("").to_a.should == ["", ""]
|
||||||
|
/(a*)?/.match("").to_a.should == ["", ""]
|
||||||
|
/(a*)*/.match("").to_a.should == ["", ""]
|
||||||
|
/(?:a|()){500,1000}/.match("a" * 500).to_a.should == ["a" * 500, ""]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ describe "Matrix#**" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ruby_bug '#17521', ''...'3.0.1' do
|
ruby_version_is '3.0.1' do # https://bugs.ruby-lang.org/issues/17521
|
||||||
describe "that is 0" do
|
describe "that is 0" do
|
||||||
it "returns the identity for square matrices" do
|
it "returns the identity for square matrices" do
|
||||||
m = Matrix[ [1, 1], [1, 1] ]
|
m = Matrix[ [1, 1], [1, 1] ]
|
||||||
|
|
|
@ -14,6 +14,31 @@ describe "Set#initialize" do
|
||||||
s.should include(3)
|
s.should include(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "uses #each_entry on the provided Enumerable" do
|
||||||
|
enumerable = MockObject.new('mock-enumerable')
|
||||||
|
enumerable.should_receive(:each_entry).and_yield(1).and_yield(2).and_yield(3)
|
||||||
|
s = Set.new(enumerable)
|
||||||
|
s.size.should eql(3)
|
||||||
|
s.should include(1)
|
||||||
|
s.should include(2)
|
||||||
|
s.should include(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses #each on the provided Enumerable if it does not respond to #each_entry" do
|
||||||
|
enumerable = MockObject.new('mock-enumerable')
|
||||||
|
enumerable.should_receive(:each).and_yield(1).and_yield(2).and_yield(3)
|
||||||
|
s = Set.new(enumerable)
|
||||||
|
s.size.should eql(3)
|
||||||
|
s.should include(1)
|
||||||
|
s.should include(2)
|
||||||
|
s.should include(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises if the provided Enumerable does not respond to #each_entry or #each" do
|
||||||
|
enumerable = MockObject.new('mock-enumerable')
|
||||||
|
-> { Set.new(enumerable) }.should raise_error(ArgumentError, "value must be enumerable")
|
||||||
|
end
|
||||||
|
|
||||||
it "should initialize with empty array and set" do
|
it "should initialize with empty array and set" do
|
||||||
s = Set.new([])
|
s = Set.new([])
|
||||||
s.size.should eql(0)
|
s.size.should eql(0)
|
||||||
|
|
|
@ -36,6 +36,50 @@ describe "C-API Exception function" do
|
||||||
runtime_error.set_backtrace []
|
runtime_error.set_backtrace []
|
||||||
-> { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42')
|
-> { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "sets $! to the raised exception when not rescuing from an another exception" do
|
||||||
|
runtime_error = RuntimeError.new '42'
|
||||||
|
runtime_error.set_backtrace []
|
||||||
|
begin
|
||||||
|
@s.rb_exc_raise(runtime_error)
|
||||||
|
rescue
|
||||||
|
$!.should == runtime_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets $! to the raised exception when $! when rescuing from an another exception" do
|
||||||
|
runtime_error = RuntimeError.new '42'
|
||||||
|
runtime_error.set_backtrace []
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
raise StandardError
|
||||||
|
rescue
|
||||||
|
@s.rb_exc_raise(runtime_error)
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
$!.should == runtime_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "rb_errinfo" do
|
||||||
|
it "is cleared when entering a C method" do
|
||||||
|
begin
|
||||||
|
raise StandardError
|
||||||
|
rescue
|
||||||
|
$!.class.should == StandardError
|
||||||
|
@s.rb_errinfo().should == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not clear $! in the calling method" do
|
||||||
|
begin
|
||||||
|
raise StandardError
|
||||||
|
rescue
|
||||||
|
@s.rb_errinfo()
|
||||||
|
$!.class.should == StandardError
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "rb_set_errinfo" do
|
describe "rb_set_errinfo" do
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
VALUE exception_spec_rb_errinfo(VALUE self) {
|
||||||
|
return rb_errinfo();
|
||||||
|
}
|
||||||
|
|
||||||
VALUE exception_spec_rb_exc_new(VALUE self, VALUE str) {
|
VALUE exception_spec_rb_exc_new(VALUE self, VALUE str) {
|
||||||
char *cstr = StringValuePtr(str);
|
char *cstr = StringValuePtr(str);
|
||||||
return rb_exc_new(rb_eException, cstr, strlen(cstr));
|
return rb_exc_new(rb_eException, cstr, strlen(cstr));
|
||||||
|
@ -41,6 +45,7 @@ VALUE exception_spec_rb_make_exception(VALUE self, VALUE ary) {
|
||||||
|
|
||||||
void Init_exception_spec(void) {
|
void Init_exception_spec(void) {
|
||||||
VALUE cls = rb_define_class("CApiExceptionSpecs", rb_cObject);
|
VALUE cls = rb_define_class("CApiExceptionSpecs", rb_cObject);
|
||||||
|
rb_define_method(cls, "rb_errinfo", exception_spec_rb_errinfo, 0);
|
||||||
rb_define_method(cls, "rb_exc_new", exception_spec_rb_exc_new, 1);
|
rb_define_method(cls, "rb_exc_new", exception_spec_rb_exc_new, 1);
|
||||||
rb_define_method(cls, "rb_exc_new2", exception_spec_rb_exc_new2, 1);
|
rb_define_method(cls, "rb_exc_new2", exception_spec_rb_exc_new2, 1);
|
||||||
rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1);
|
rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1);
|
||||||
|
|
|
@ -227,6 +227,16 @@ static VALUE io_spec_errno_set(VALUE self, VALUE val) {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALUE io_spec_mode_sync_flag(VALUE self, VALUE io) {
|
||||||
|
rb_io_t *fp;
|
||||||
|
GetOpenFile(io, fp);
|
||||||
|
if (fp->mode & FMODE_SYNC) {
|
||||||
|
return Qtrue;
|
||||||
|
} else {
|
||||||
|
return Qfalse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Init_io_spec(void) {
|
void Init_io_spec(void) {
|
||||||
VALUE cls = rb_define_class("CApiIOSpecs", rb_cObject);
|
VALUE cls = rb_define_class("CApiIOSpecs", rb_cObject);
|
||||||
rb_define_method(cls, "GetOpenFile_fd", io_spec_GetOpenFile_fd, 1);
|
rb_define_method(cls, "GetOpenFile_fd", io_spec_GetOpenFile_fd, 1);
|
||||||
|
@ -251,6 +261,7 @@ void Init_io_spec(void) {
|
||||||
rb_define_method(cls, "rb_fd_fix_cloexec", io_spec_rb_fd_fix_cloexec, 1);
|
rb_define_method(cls, "rb_fd_fix_cloexec", io_spec_rb_fd_fix_cloexec, 1);
|
||||||
rb_define_method(cls, "rb_cloexec_open", io_spec_rb_cloexec_open, 3);
|
rb_define_method(cls, "rb_cloexec_open", io_spec_rb_cloexec_open, 3);
|
||||||
rb_define_method(cls, "errno=", io_spec_errno_set, 1);
|
rb_define_method(cls, "errno=", io_spec_errno_set, 1);
|
||||||
|
rb_define_method(cls, "rb_io_mode_sync_flag", io_spec_mode_sync_flag, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -310,10 +310,6 @@ static VALUE kernel_spec_rb_make_backtrace(VALUE self) {
|
||||||
return rb_make_backtrace();
|
return rb_make_backtrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE kernel_spec_rb_obj_method(VALUE self, VALUE obj, VALUE method) {
|
|
||||||
return rb_obj_method(obj, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
static VALUE kernel_spec_rb_funcall3(VALUE self, VALUE obj, VALUE method) {
|
static VALUE kernel_spec_rb_funcall3(VALUE self, VALUE obj, VALUE method) {
|
||||||
return rb_funcall3(obj, SYM2ID(method), 0, NULL);
|
return rb_funcall3(obj, SYM2ID(method), 0, NULL);
|
||||||
}
|
}
|
||||||
|
@ -366,7 +362,6 @@ void Init_kernel_spec(void) {
|
||||||
rb_define_method(cls, "rb_set_end_proc", kernel_spec_rb_set_end_proc, 1);
|
rb_define_method(cls, "rb_set_end_proc", kernel_spec_rb_set_end_proc, 1);
|
||||||
rb_define_method(cls, "rb_f_sprintf", kernel_spec_rb_f_sprintf, 1);
|
rb_define_method(cls, "rb_f_sprintf", kernel_spec_rb_f_sprintf, 1);
|
||||||
rb_define_method(cls, "rb_make_backtrace", kernel_spec_rb_make_backtrace, 0);
|
rb_define_method(cls, "rb_make_backtrace", kernel_spec_rb_make_backtrace, 0);
|
||||||
rb_define_method(cls, "rb_obj_method", kernel_spec_rb_obj_method, 2);
|
|
||||||
rb_define_method(cls, "rb_funcall3", kernel_spec_rb_funcall3, 2);
|
rb_define_method(cls, "rb_funcall3", kernel_spec_rb_funcall3, 2);
|
||||||
rb_define_method(cls, "rb_funcall_many_args", kernel_spec_rb_funcall_many_args, 2);
|
rb_define_method(cls, "rb_funcall_many_args", kernel_spec_rb_funcall_many_args, 2);
|
||||||
rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3);
|
rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3);
|
||||||
|
|
|
@ -147,6 +147,10 @@ static VALUE object_specs_rb_obj_method_arity(VALUE self, VALUE obj, VALUE mid)
|
||||||
return INT2FIX(rb_obj_method_arity(obj, SYM2ID(mid)));
|
return INT2FIX(rb_obj_method_arity(obj, SYM2ID(mid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE object_specs_rb_obj_method(VALUE self, VALUE obj, VALUE method) {
|
||||||
|
return rb_obj_method(obj, method);
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE object_spec_rb_obj_taint(VALUE self, VALUE obj) {
|
static VALUE object_spec_rb_obj_taint(VALUE self, VALUE obj) {
|
||||||
return rb_obj_taint(obj);
|
return rb_obj_taint(obj);
|
||||||
}
|
}
|
||||||
|
@ -420,6 +424,7 @@ void Init_object_spec(void) {
|
||||||
rb_define_method(cls, "rb_obj_is_instance_of", so_instance_of, 2);
|
rb_define_method(cls, "rb_obj_is_instance_of", so_instance_of, 2);
|
||||||
rb_define_method(cls, "rb_obj_is_kind_of", so_kind_of, 2);
|
rb_define_method(cls, "rb_obj_is_kind_of", so_kind_of, 2);
|
||||||
rb_define_method(cls, "rb_obj_method_arity", object_specs_rb_obj_method_arity, 2);
|
rb_define_method(cls, "rb_obj_method_arity", object_specs_rb_obj_method_arity, 2);
|
||||||
|
rb_define_method(cls, "rb_obj_method", object_specs_rb_obj_method, 2);
|
||||||
rb_define_method(cls, "rb_obj_taint", object_spec_rb_obj_taint, 1);
|
rb_define_method(cls, "rb_obj_taint", object_spec_rb_obj_taint, 1);
|
||||||
rb_define_method(cls, "rb_require", so_require, 0);
|
rb_define_method(cls, "rb_require", so_require, 0);
|
||||||
rb_define_method(cls, "rb_respond_to", so_respond_to, 2);
|
rb_define_method(cls, "rb_respond_to", so_respond_to, 2);
|
||||||
|
@ -462,7 +467,7 @@ void Init_object_spec(void) {
|
||||||
rb_define_method(cls, "rb_undef_alloc_func", undef_alloc_func, 1);
|
rb_define_method(cls, "rb_undef_alloc_func", undef_alloc_func, 1);
|
||||||
rb_define_method(cls, "speced_allocator?", speced_allocator_p, 1);
|
rb_define_method(cls, "speced_allocator?", speced_allocator_p, 1);
|
||||||
rb_define_method(cls, "custom_alloc_func?", custom_alloc_func_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);
|
rb_define_method(cls, "not_implemented_method", rb_f_notimplement, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -375,3 +375,29 @@ describe "rb_cloexec_open" do
|
||||||
@io.close_on_exec?.should be_true
|
@io.close_on_exec?.should be_true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "rb_io_t modes flags" do
|
||||||
|
before :each do
|
||||||
|
@o = CApiIOSpecs.new
|
||||||
|
@name = tmp("c_api_rb_io_specs")
|
||||||
|
touch @name
|
||||||
|
end
|
||||||
|
|
||||||
|
after :each do
|
||||||
|
rm_r @name
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has the sync flag set if the IO object is synced in Ruby" do
|
||||||
|
File.open(@name) { |io|
|
||||||
|
io.sync = true
|
||||||
|
@o.rb_io_mode_sync_flag(io).should == true
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has the sync flag unset if the IO object is not synced in Ruby" do
|
||||||
|
File.open(@name) { |io|
|
||||||
|
io.sync = false
|
||||||
|
@o.rb_io_mode_sync_flag(io).should == false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -559,20 +559,6 @@ describe "C-API Kernel function" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "rb_obj_method" do
|
|
||||||
it "returns the method object for a symbol" do
|
|
||||||
method = @s.rb_obj_method("test", :size)
|
|
||||||
method.owner.should == String
|
|
||||||
method.name.to_sym.should == :size
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the method object for a string" do
|
|
||||||
method = @s.rb_obj_method("test", "size")
|
|
||||||
method.owner.should == String
|
|
||||||
method.name.to_sym.should == :size
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "rb_funcall3" do
|
describe "rb_funcall3" do
|
||||||
before :each do
|
before :each do
|
||||||
@obj = Object.new
|
@obj = Object.new
|
||||||
|
|
|
@ -246,11 +246,23 @@ describe "CApiModule" do
|
||||||
cls.new.test_method.should == :test_method
|
cls.new.test_method.should == :test_method
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns the correct arity of the method in class" do
|
||||||
|
cls = Class.new
|
||||||
|
@m.rb_define_method(cls, "test_method")
|
||||||
|
cls.new.method(:test_method).arity.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
it "defines a method on a module" do
|
it "defines a method on a module" do
|
||||||
mod = Module.new
|
mod = Module.new
|
||||||
@m.rb_define_method(mod, "test_method")
|
@m.rb_define_method(mod, "test_method")
|
||||||
mod.should have_instance_method(:test_method)
|
mod.should have_instance_method(:test_method)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns the correct arity of the method in module" do
|
||||||
|
mod = Module.new
|
||||||
|
@m.rb_define_method(mod, "test_method")
|
||||||
|
mod.instance_method(:test_method).arity.should == 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "rb_define_module_function" do
|
describe "rb_define_module_function" do
|
||||||
|
@ -263,12 +275,23 @@ describe "CApiModule" do
|
||||||
@mod.test_module_function.should == :test_method
|
@mod.test_module_function.should == :test_method
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns the correct arity of the module function" do
|
||||||
|
@mod.method(:test_module_function).arity.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
it "defines a private instance method" do
|
it "defines a private instance method" do
|
||||||
cls = Class.new
|
cls = Class.new
|
||||||
cls.include(@mod)
|
cls.include(@mod)
|
||||||
|
|
||||||
cls.should have_private_instance_method(:test_module_function)
|
cls.should have_private_instance_method(:test_module_function)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns the correct arity for private instance method" do
|
||||||
|
cls = Class.new
|
||||||
|
cls.include(@mod)
|
||||||
|
|
||||||
|
@mod.instance_method(:test_module_function).arity.should == 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "rb_define_private_method" do
|
describe "rb_define_private_method" do
|
||||||
|
|
|
@ -161,6 +161,20 @@ describe "CApiObject" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "rb_obj_method" do
|
||||||
|
it "returns the method object for a symbol" do
|
||||||
|
method = @o.rb_obj_method("test", :size)
|
||||||
|
method.owner.should == String
|
||||||
|
method.name.to_sym.should == :size
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the method object for a string" do
|
||||||
|
method = @o.rb_obj_method("test", "size")
|
||||||
|
method.owner.should == String
|
||||||
|
method.name.to_sym.should == :size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "rb_method_boundp" do
|
describe "rb_method_boundp" do
|
||||||
it "returns true when the given method is bound" do
|
it "returns true when the given method is bound" do
|
||||||
@o.rb_method_boundp(Object, :class, true).should == true
|
@o.rb_method_boundp(Object, :class, true).should == true
|
||||||
|
|
|
@ -102,13 +102,13 @@ describe "C-API Thread function" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "rb_thread_call_without_gvl" do
|
describe "rb_thread_call_without_gvl" do
|
||||||
it "runs a C function with the global lock unlocked" do
|
it "runs a C function with the global lock unlocked and can be woken by Thread#wakeup" do
|
||||||
thr = Thread.new do
|
thr = Thread.new do
|
||||||
@t.rb_thread_call_without_gvl
|
@t.rb_thread_call_without_gvl
|
||||||
end
|
end
|
||||||
|
|
||||||
# Wait until it's blocking...
|
# Wait until it's blocking...
|
||||||
Thread.pass while thr.status and thr.status != "sleep"
|
Thread.pass until thr.stop?
|
||||||
|
|
||||||
# The thread status is set to sleep by rb_thread_call_without_gvl(),
|
# The thread status is set to sleep by rb_thread_call_without_gvl(),
|
||||||
# but the thread might not be in the blocking read(2) yet, so wait a bit.
|
# but the thread might not be in the blocking read(2) yet, so wait a bit.
|
||||||
|
@ -121,14 +121,48 @@ describe "C-API Thread function" do
|
||||||
thr.value.should be_true
|
thr.value.should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
guard -> { platform_is :mingw and ruby_version_is ""..."2.7" } do
|
platform_is_not :windows do
|
||||||
|
it "runs a C function with the global lock unlocked and can be woken by a signal" do
|
||||||
|
# Ruby signal handlers run on the main thread, so we need to reverse roles here and have a thread interrupt us
|
||||||
|
thr = Thread.current
|
||||||
|
thr.should == Thread.main
|
||||||
|
|
||||||
|
going_to_block = false
|
||||||
|
interrupter = Thread.new do
|
||||||
|
# Wait until it's blocking...
|
||||||
|
Thread.pass until going_to_block and thr.stop?
|
||||||
|
|
||||||
|
# The thread status is set to sleep by rb_thread_call_without_gvl(),
|
||||||
|
# but the thread might not be in the blocking read(2) yet, so wait a bit.
|
||||||
|
sleep 0.1
|
||||||
|
|
||||||
|
# Wake it up by sending a signal
|
||||||
|
done = false
|
||||||
|
prev_handler = Signal.trap(:HUP) { done = true }
|
||||||
|
begin
|
||||||
|
Process.kill :HUP, Process.pid
|
||||||
|
sleep 0.001 until done
|
||||||
|
ensure
|
||||||
|
Signal.trap(:HUP, prev_handler)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
going_to_block = true
|
||||||
|
# Make sure it stopped and we got a proper value
|
||||||
|
@t.rb_thread_call_without_gvl.should be_true
|
||||||
|
|
||||||
|
interrupter.join
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
guard_not -> { platform_is :mingw and ruby_version_is ""..."2.7" } do
|
||||||
it "runs a C function with the global lock unlocked and unlocks IO with the generic RUBY_UBF_IO" do
|
it "runs a C function with the global lock unlocked and unlocks IO with the generic RUBY_UBF_IO" do
|
||||||
thr = Thread.new do
|
thr = Thread.new do
|
||||||
@t.rb_thread_call_without_gvl_with_ubf_io
|
@t.rb_thread_call_without_gvl_with_ubf_io
|
||||||
end
|
end
|
||||||
|
|
||||||
# Wait until it's blocking...
|
# Wait until it's blocking...
|
||||||
Thread.pass while thr.status and thr.status != "sleep"
|
Thread.pass until thr.stop?
|
||||||
|
|
||||||
# The thread status is set to sleep by rb_thread_call_without_gvl(),
|
# The thread status is set to sleep by rb_thread_call_without_gvl(),
|
||||||
# but the thread might not be in the blocking read(2) yet, so wait a bit.
|
# but the thread might not be in the blocking read(2) yet, so wait a bit.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue