1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
This commit is contained in:
Benoit Daloze 2020-01-28 20:47:48 +01:00
parent ed377cc9aa
commit 809f0b8a13
76 changed files with 2451 additions and 224 deletions

View file

@ -55,6 +55,7 @@ DefSpecsLambdaVisibility
DefineMethodByProcClass DefineMethodByProcClass
DefineMethodSpecClass DefineMethodSpecClass
DefineSingletonMethodSpecClass DefineSingletonMethodSpecClass
Delegator
DescArray DescArray
DescObjectTest DescObjectTest
Digest Digest
@ -148,6 +149,7 @@ Readline
ReceiverClass ReceiverClass
RegexpSpecsSubclass RegexpSpecsSubclass
RegexpSpecsSubclassTwo RegexpSpecsSubclassTwo
Reline
RescueInClassExample RescueInClassExample
Resolv Resolv
SHA1Constants SHA1Constants
@ -161,6 +163,7 @@ SecondClass
SecureRandom SecureRandom
Set Set
Shellwords Shellwords
SimpleDelegator
SingleForwardable SingleForwardable
Singleton Singleton
Socket Socket
@ -188,6 +191,7 @@ TimeoutError
UDPSocket UDPSocket
UNIXServer UNIXServer
UNIXSocket UNIXSocket
URI
UnaryMinusTest UnaryMinusTest
UnicodeNormalize UnicodeNormalize
UnloadableDumpableDir UnloadableDumpableDir

View file

@ -114,6 +114,7 @@ Style/Lambda:
Exclude: Exclude:
- 'language/lambda_spec.rb' - 'language/lambda_spec.rb'
- 'language/proc_spec.rb' - 'language/proc_spec.rb'
- 'language/numbered_parameters_spec.rb'
- 'core/kernel/lambda_spec.rb' - 'core/kernel/lambda_spec.rb'
Style/EmptyLambdaParameter: Style/EmptyLambdaParameter:

View file

@ -0,0 +1,11 @@
require_relative '../../spec_helper'
ruby_version_is "2.7" do
describe "Array#deconstruct" do
it "returns self" do
array = [1]
array.deconstruct.should equal array
end
end
end

View file

@ -396,6 +396,14 @@ describe "Array#[]= with [m..n]" do
a.should == [1, 2, 3, 8, 4, 5] a.should == [1, 2, 3, 8, 4, 5]
end end
it "inserts at the end if m > the array size" do
a = [1, 2, 3]
a[3..3] = [4]
a.should == [1, 2, 3, 4]
a[5..7] = [6]
a.should == [1, 2, 3, 4, nil, 6]
end
describe "Range subclasses" do describe "Range subclasses" do
before :each do before :each do
@range_incl = ArraySpecs::MyRange.new(1, 2) @range_incl = ArraySpecs::MyRange.new(1, 2)
@ -425,6 +433,45 @@ describe "Array#[]= with [m..n]" do
end end
end end
ruby_version_is "2.6" do
describe "Array#[]= with [m..]" do
it "just sets the section defined by range to nil even if the rhs is nil" do
a = [1, 2, 3, 4, 5]
a[eval("(2..)")] = nil
a.should == [1, 2, nil]
end
it "just sets the section defined by range to nil if m and n < 0 and the rhs is nil" do
a = [1, 2, 3, 4, 5]
a[eval("(-3..)")] = nil
a.should == [1, 2, nil]
end
it "replaces the section defined by range" do
a = [6, 5, 4, 3, 2, 1]
a[eval("(3...)")] = 9
a.should == [6, 5, 4, 9]
a[eval("(2..)")] = [7, 7, 7]
a.should == [6, 5, 7, 7, 7]
end
it "replaces the section if m and n < 0" do
a = [1, 2, 3, 4, 5]
a[eval("(-3..)")] = [7, 8, 9]
a.should == [1, 2, 7, 8, 9]
end
it "inserts at the end if m > the array size" do
a = [1, 2, 3]
a[eval("(3..)")] = [4]
a.should == [1, 2, 3, 4]
a[eval("(5..)")] = [6]
a.should == [1, 2, 3, 4, nil, 6]
end
end
end
describe "Array#[] after a shift" do describe "Array#[] after a shift" do
it "works for insertion" do it "works for insertion" do
a = [1,2] a = [1,2]

View file

@ -314,4 +314,13 @@ describe "Array#fill with (filler, range)" do
def obj.<=>(rhs); rhs == self ? 0 : nil end def obj.<=>(rhs); rhs == self ? 0 : nil end
-> { [].fill('a', obj..obj) }.should raise_error(TypeError) -> { [].fill('a', obj..obj) }.should raise_error(TypeError)
end end
ruby_version_is "2.6" do
it "works with endless ranges" do
[1, 2, 3, 4].fill('x', eval("(1..)")).should == [1, 'x', 'x', 'x']
[1, 2, 3, 4].fill('x', eval("(3...)")).should == [1, 2, 3, 'x']
[1, 2, 3, 4].fill(eval("(1..)")) { |x| x + 2 }.should == [1, 3, 4, 5]
[1, 2, 3, 4].fill(eval("(3...)")) { |x| x + 2 }.should == [1, 2, 3, 5]
end
end
end end

View file

@ -1,87 +1,21 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/intersection'
describe "Array#&" do describe "Array#&" do
it "creates an array with elements common to both arrays (intersection)" do it_behaves_like :array_intersection, :&
([] & []).should == [] end
([1, 2] & []).should == []
([] & [1, 2]).should == []
([ 1, 3, 5 ] & [ 1, 2, 3 ]).should == [1, 3]
end
it "creates an array with no duplicates" do ruby_version_is "2.7" do
([ 1, 1, 3, 5 ] & [ 1, 2, 3 ]).uniq!.should == nil describe "Array#intersection" do
end it_behaves_like :array_intersection, :intersection
it "creates an array with elements in order they are first encountered" do it "accepts multiple arguments" do
([ 1, 2, 3, 2, 5 ] & [ 5, 2, 3, 4 ]).should == [2, 3, 5] [1, 2, 3, 4].intersection([1, 2, 3], [2, 3, 4]).should == [2, 3]
end
it "does not modify the original Array" do
a = [1, 1, 3, 5]
(a & [1, 2, 3]).should == [1, 3]
a.should == [1, 1, 3, 5]
end
it "properly handles recursive arrays" do
empty = ArraySpecs.empty_recursive_array
(empty & empty).should == empty
(ArraySpecs.recursive_array & []).should == []
([] & ArraySpecs.recursive_array).should == []
(ArraySpecs.recursive_array & ArraySpecs.recursive_array).should == [1, 'two', 3.0, ArraySpecs.recursive_array]
end
it "tries to convert the passed argument to an Array using #to_ary" do
obj = mock('[1,2,3]')
obj.should_receive(:to_ary).and_return([1, 2, 3])
([1, 2] & obj).should == ([1, 2])
end
it "determines equivalence between elements in the sense of eql?" do
not_supported_on :opal do
([5.0, 4.0] & [5, 4]).should == []
end end
str = "x" it "preserves elements order from original array" do
([str] & [str.dup]).should == [str] [1, 2, 3, 4].intersection([3, 2, 1]).should == [1, 2, 3]
end
obj1 = mock('1')
obj2 = mock('2')
obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0)
obj1.should_receive(:eql?).at_least(1).and_return(true)
obj2.stub!(:eql?).and_return(true)
([obj1] & [obj2]).should == [obj1]
([obj1, obj1, obj2, obj2] & [obj2]).should == [obj1]
obj1 = mock('3')
obj2 = mock('4')
obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0)
obj1.should_receive(:eql?).at_least(1).and_return(false)
([obj1] & [obj2]).should == []
([obj1, obj1, obj2, obj2] & [obj2]).should == [obj2]
end
it "does return subclass instances for Array subclasses" do
(ArraySpecs::MyArray[1, 2, 3] & []).should be_an_instance_of(Array)
(ArraySpecs::MyArray[1, 2, 3] & ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
([] & ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
end
it "does not call to_ary on array subclasses" do
([5, 6] & ArraySpecs::ToAryArray[1, 2, 5, 6]).should == [5, 6]
end
it "properly handles an identical item even when its #eql? isn't reflexive" do
x = mock('x')
x.stub!(:hash).and_return(42)
x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
([x] & [x]).should == [x]
end end
end end

View file

@ -0,0 +1,84 @@
describe :array_intersection, shared: true do
it "creates an array with elements common to both arrays (intersection)" do
[].send(@method, []).should == []
[1, 2].send(@method, []).should == []
[].send(@method, [1, 2]).should == []
[ 1, 3, 5 ].send(@method, [ 1, 2, 3 ]).should == [1, 3]
end
it "creates an array with no duplicates" do
[ 1, 1, 3, 5 ].send(@method, [ 1, 2, 3 ]).uniq!.should == nil
end
it "creates an array with elements in order they are first encountered" do
[ 1, 2, 3, 2, 5 ].send(@method, [ 5, 2, 3, 4 ]).should == [2, 3, 5]
end
it "does not modify the original Array" do
a = [1, 1, 3, 5]
a.send(@method, [1, 2, 3]).should == [1, 3]
a.should == [1, 1, 3, 5]
end
it "properly handles recursive arrays" do
empty = ArraySpecs.empty_recursive_array
empty.send(@method, empty).should == empty
ArraySpecs.recursive_array.send(@method, []).should == []
[].send(@method, ArraySpecs.recursive_array).should == []
ArraySpecs.recursive_array.send(@method, ArraySpecs.recursive_array).should == [1, 'two', 3.0, ArraySpecs.recursive_array]
end
it "tries to convert the passed argument to an Array using #to_ary" do
obj = mock('[1,2,3]')
obj.should_receive(:to_ary).and_return([1, 2, 3])
[1, 2].send(@method, obj).should == ([1, 2])
end
it "determines equivalence between elements in the sense of eql?" do
not_supported_on :opal do
[5.0, 4.0].send(@method, [5, 4]).should == []
end
str = "x"
[str].send(@method, [str.dup]).should == [str]
obj1 = mock('1')
obj2 = mock('2')
obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0)
obj1.should_receive(:eql?).at_least(1).and_return(true)
obj2.stub!(:eql?).and_return(true)
[obj1].send(@method, [obj2]).should == [obj1]
[obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1]
obj1 = mock('3')
obj2 = mock('4')
obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0)
obj1.should_receive(:eql?).at_least(1).and_return(false)
[obj1].send(@method, [obj2]).should == []
[obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj2]
end
it "does return subclass instances for Array subclasses" do
ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array)
ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
[].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
end
it "does not call to_ary on array subclasses" do
[5, 6].send(@method, ArraySpecs::ToAryArray[1, 2, 5, 6]).should == [5, 6]
end
it "properly handles an identical item even when its #eql? isn't reflexive" do
x = mock('x')
x.stub!(:hash).and_return(42)
x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
[x].send(@method, [x]).should == [x]
end
end

View file

@ -113,6 +113,20 @@ describe :array_join_with_default_separator, shared: true do
-> { ary_utf8_bad_binary.send(@method) }.should raise_error(EncodingError) -> { ary_utf8_bad_binary.send(@method) }.should raise_error(EncodingError)
end end
ruby_version_is "2.7" do
context "when $, is not nil" do
before do
suppress_warning do
$, = '*'
end
end
it "warns" do
-> { [].join }.should complain(/warning: \$, is set to non-nil value/)
end
end
end
end end
describe :array_join_with_string_separator, shared: true do describe :array_join_with_string_separator, shared: true do

View file

@ -153,6 +153,18 @@ describe "Array#slice!" do
it "raises a #{frozen_error_class} on a frozen array" do it "raises a #{frozen_error_class} on a frozen array" do
-> { ArraySpecs.frozen_array.slice!(0, 0) }.should raise_error(frozen_error_class) -> { ArraySpecs.frozen_array.slice!(0, 0) }.should raise_error(frozen_error_class)
end end
ruby_version_is "2.6" do
it "works with endless ranges" do
a = [1, 2, 3]
a.slice!(eval("(1..)")).should == [2, 3]
a.should == [1]
a = [1, 2, 3]
a.slice!(eval("(2...)")).should == [3]
a.should == [1, 2]
end
end
end end
describe "Array#slice" do describe "Array#slice" do

View file

@ -60,4 +60,11 @@ describe "Array#values_at" do
it "does not return subclass instance on Array subclasses" do it "does not return subclass instance on Array subclasses" do
ArraySpecs::MyArray[1, 2, 3].values_at(0, 1..2, 1).should be_an_instance_of(Array) ArraySpecs::MyArray[1, 2, 3].values_at(0, 1..2, 1).should be_an_instance_of(Array)
end end
ruby_version_is "2.6" do
it "works when given endless ranges" do
[1, 2, 3, 4].values_at(eval("(1..)")).should == [2, 3, 4]
[1, 2, 3, 4].values_at(eval("(3...)")).should == [4]
end
end
end end

View file

@ -1,7 +1,7 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "Complex#<=>" do describe "Complex#<=>" do
ruby_version_is '2.7' do ruby_version_is "2.7" do
it "returns nil if either self or argument has imaginary part" do it "returns nil if either self or argument has imaginary part" do
(Complex(5, 1) <=> Complex(2)).should be_nil (Complex(5, 1) <=> Complex(2)).should be_nil
(Complex(1) <=> Complex(2, 1)).should be_nil (Complex(1) <=> Complex(2, 1)).should be_nil

View file

@ -40,10 +40,10 @@ describe "Enumerator#each" do
end end
it "calls the method given in the constructor until it's exhausted" do it "calls the method given in the constructor until it's exhausted" do
each = mock('each') each = mock('peach')
each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3) each.should_receive(:peach).and_yield(1).and_yield(2).and_yield(3)
acc = [] acc = []
each.to_enum.each {|e| acc << e } each.to_enum(:peach).each {|e| acc << e }
acc.should == [1,2,3] acc.should == [1,2,3]
end end

View file

@ -0,0 +1,29 @@
require_relative '../../../spec_helper'
ruby_version_is "2.7" do
describe "Enumerator::Lazy#eager" do
it "returns a non-lazy Enumerator converted from the lazy enumerator" do
enum = [1, 2, 3].lazy
enum.class.should == Enumerator::Lazy
enum.eager.class.should == Enumerator
end
it "does not enumerate an enumerator" do
ScratchPad.record []
sequence = [1, 2, 3]
enum_lazy = Enumerator::Lazy.new(sequence) do |yielder, value|
yielder << value
ScratchPad << value
end
ScratchPad.recorded.should == []
enum = enum_lazy.eager
ScratchPad.recorded.should == []
enum.map { |i| i }.should == [1, 2, 3]
ScratchPad.recorded.should == [1, 2, 3]
end
end
end

View file

@ -38,4 +38,42 @@ describe "Enumerator.new" do
end end
enum.to_a.should == [:bar] enum.to_a.should == [:bar]
end end
context "when passed a block" do
it "defines iteration with block, yielder argument and calling << method" do
enum = Enumerator.new do |yielder|
a = 1
loop do
yielder << a
a = a + 1
end
end
enum.take(3).should == [1, 2, 3]
end
it "defines iteration with block, yielder argument and calling yield method" do
enum = Enumerator.new do |yielder|
a = 1
loop do
yielder.yield(a)
a = a + 1
end
end
enum.take(3).should == [1, 2, 3]
end
ruby_version_is "2.7" do
it "defines iteration with block, yielder argument and treating it as a proc" do
enum = Enumerator.new do |yielder|
"a\nb\nc".each_line(&yielder)
end
enum.to_a.should == ["a\n", "b\n", "c"]
end
end
end
end end

View file

@ -0,0 +1,36 @@
require_relative '../../spec_helper'
ruby_version_is "2.7" do
describe "Enumerator.produce" do
it "creates an infinite enumerator" do
enum = Enumerator.produce(0) { |prev| prev + 1 }
enum.take(5).should == [0, 1, 2, 3, 4]
end
it "terminates iteration when block raises StopIteration exception" do
enum = Enumerator.produce(0) do | prev|
raise StopIteration if prev >= 2
prev + 1
end
enum.to_a.should == [0, 1, 2]
end
context "when initial value skipped" do
it "uses nil instead" do
ScratchPad.record []
enum = Enumerator.produce { |prev| ScratchPad << prev; (prev || 0) + 1 }
enum.take(3).should == [1, 2, 3]
ScratchPad.recorded.should == [nil, 1, 2]
end
it "starts enumerable from result of first block call" do
array = "a\nb\nc\nd".lines
lines = Enumerator.produce { array.shift }.take_while { |s| s }
lines.should == ["a\n", "b\n", "c\n", "d"]
end
end
end
end

View file

@ -0,0 +1,18 @@
require_relative '../../../spec_helper'
ruby_version_is "2.7" do
describe "Enumerator::Yielder#to_proc" do
it "returns a Proc object that takes an argument and yields it to the block" do
ScratchPad.record []
y = Enumerator::Yielder.new { |*args| ScratchPad << args; "foobar" }
callable = y.to_proc
callable.class.should == Proc
result = callable.call(1, 2)
ScratchPad.recorded.should == [[1, 2]]
result.should == "foobar"
end
end
end

View file

@ -1,23 +0,0 @@
require_relative '../../spec_helper'
describe "Encoding::UndefinedConversionError#destination_encoding_name" do
it "returns the destination encoding name" do
ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
begin
ec.convert("\xa0")
rescue Encoding::UndefinedConversionError => e
e.destination_encoding_name.should == "EUC-JP"
end
end
end
describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do
it "returns the destination encoding name" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
begin
ec.convert("\xa0")
rescue Encoding::InvalidByteSequenceError => e
e.destination_encoding_name.should == "UTF-8"
end
end
end

View file

@ -1,23 +0,0 @@
require_relative '../../spec_helper'
describe "Encoding::UndefinedConversionError#destination_encoding" do
it "returns the destination encoding" do
ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
begin
ec.convert("\xa0")
rescue Encoding::UndefinedConversionError => e
e.destination_encoding.should == Encoding::EUC_JP
end
end
end
describe "Encoding::InvalidByteSequenceError#destination_encoding" do
it "returns the destination encoding" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
begin
ec.convert("\xa0")
rescue Encoding::InvalidByteSequenceError => e
e.destination_encoding.should == Encoding::UTF_8
end
end
end

View file

@ -1,12 +0,0 @@
require_relative '../../spec_helper'
describe "Encoding::InvalidByteSequenceError#error_bytes" do
it "returns the error bytes" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
begin
ec.convert("\xa0")
rescue Encoding::InvalidByteSequenceError => e
e.error_bytes.should == "\xA0".force_encoding("ASCII-8BIT")
end
end
end

View file

@ -1,12 +0,0 @@
require_relative '../../spec_helper'
describe "Encoding::UndefinedConversionError#error_char" do
it "returns the error char" do
ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
begin
ec.convert("\xa0")
rescue Encoding::UndefinedConversionError => e
e.error_char.should == "\u00A0"
end
end
end

View file

@ -1 +0,0 @@
require_relative '../../spec_helper'

View file

@ -1,12 +0,0 @@
require_relative '../../spec_helper'
describe "Encoding::InvalidByteSequenceError#readagain_bytes" do
it "returns the next byte" do
begin
"abc\xa4def".encode("ISO-8859-1", "EUC-JP")
rescue Encoding::InvalidByteSequenceError => e
e.error_bytes.should == "\xA4".force_encoding("ASCII-8BIT")
e.readagain_bytes.should == 'd'
end
end
end

View file

@ -1,23 +0,0 @@
require_relative '../../spec_helper'
describe "Encoding::UndefinedConversionError#source_encoding_name" do
it "returns the source encoding name" do
ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
begin
ec.convert("\xa0")
rescue Encoding::UndefinedConversionError => e
e.source_encoding_name.should == "UTF-8"
end
end
end
describe "Encoding::InvalidByteSequenceError#source_encoding_name" do
it "returns the source encoding name" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
begin
ec.convert("\xa0")
rescue Encoding::InvalidByteSequenceError => e
e.source_encoding_name.should == "EUC-JP"
end
end
end

View file

@ -1,23 +0,0 @@
require_relative '../../spec_helper'
describe "Encoding::UndefinedConversionError#source_encoding" do
it "returns the source encoding" do
ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
begin
ec.convert("\xa0")
rescue Encoding::UndefinedConversionError => e
e.source_encoding.should == Encoding::UTF_8
end
end
end
describe "Encoding::InvalidByteSequenceError#source_encoding" do
it "returns the source encoding" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
begin
ec.convert("\xa0")
rescue Encoding::InvalidByteSequenceError => e
e.source_encoding.should == Encoding::EUC_JP
end
end
end

View file

@ -0,0 +1,25 @@
require_relative '../../spec_helper'
ruby_version_is "2.7" do
describe "Hash#deconstruct_keys" do
it "returns self" do
hash = {a: 1, b: 2}
hash.deconstruct_keys([:a, :b]).should equal hash
end
it "requires one argument" do
-> {
{a: 1}.deconstruct_keys
}.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
end
it "ignores argument" do
hash = {a: 1, b: 2}
hash.deconstruct_keys([:a]).should == {a: 1, b: 2}
hash.deconstruct_keys(0 ).should == {a: 1, b: 2}
hash.deconstruct_keys('' ).should == {a: 1, b: 2}
end
end
end

View file

@ -78,6 +78,91 @@ describe "Integer#[]" do
it "returns 0 when passed a Float in the range of a Bignum" do it "returns 0 when passed a Float in the range of a Bignum" do
3[bignum_value.to_f].should == 0 3[bignum_value.to_f].should == 0
end end
ruby_version_is "2.7" do
context "when index and length passed" do
it "returns specified number of bits from specified position" do
0b101001101[2, 4].should == 0b0011
0b101001101[2, 5].should == 0b10011
0b101001101[2, 7].should == 0b1010011
end
it "ensures n[i, len] equals to (n >> i) & ((1 << len) - 1)" do
n = 0b101001101; i = 2; len = 4
n[i, len].should == (n >> i) & ((1 << len) - 1)
end
it "moves start position to the most significant bits when negative index passed" do
0b000001[-1, 4].should == 0b10
0b000001[-2, 4].should == 0b100
0b000001[-3, 4].should == 0b1000
end
it "ignores negative length" do
0b101001101[1, -1].should == 0b10100110
0b101001101[2, -1].should == 0b1010011
0b101001101[3, -1].should == 0b101001
0b101001101[3, -5].should == 0b101001
0b101001101[3, -15].should == 0b101001
0b101001101[3, -125].should == 0b101001
end
end
context "when range passed" do
it "returns bits specified by range" do
0b101001101[2..5].should == 0b0011
0b101001101[2..6].should == 0b10011
0b101001101[2..8].should == 0b1010011
end
it "ensures n[i..j] equals to (n >> i) & ((1 << (j - i + 1)) - 1)" do
n = 0b101001101; i = 2; j = 5
n[i..j].should == (n >> i) & ((1 << (j - i + 1)) - 1)
end
it "ensures n[i..] equals to (n >> i)" do
eval("0b101001101[3..]").should == 0b101001101 >> 3
end
it "moves lower boundary to the most significant bits when negative value passed" do
0b000001[-1, 4].should == 0b10
0b000001[-2, 4].should == 0b100
0b000001[-3, 4].should == 0b1000
end
it "ignores negative upper boundary" do
0b101001101[1..-1].should == 0b10100110
0b101001101[1..-2].should == 0b10100110
0b101001101[1..-3].should == 0b10100110
end
it "ignores upper boundary smaller than lower boundary" do
0b101001101[4..1].should == 0b10100
0b101001101[4..2].should == 0b10100
0b101001101[4..3].should == 0b10100
end
it "raises FloatDomainError if any boundary is infinity" do
-> { 0x0001[3..Float::INFINITY] }.should raise_error(FloatDomainError, /Infinity/)
-> { 0x0001[-Float::INFINITY..3] }.should raise_error(FloatDomainError, /-Infinity/)
end
context "when passed (..i)" do
it "returns 0 if all i bits equal 0" do
eval("0b10000[..1]").should == 0
eval("0b10000[..2]").should == 0
eval("0b10000[..3]").should == 0
end
it "raises ArgumentError if any of i bit equals 1" do
-> {
eval("0b111110[..3]")
}.should raise_error(ArgumentError, /The beginless range for Integer#\[\] results in infinity/)
end
end
end
end
end end
context "bignum" do context "bignum" do

View file

@ -22,6 +22,30 @@ describe 'Kernel#caller_locations' do
locations.length.should == 1 locations.length.should == 1
end end
it "can be called with a range" do
locations1 = caller_locations(0)
locations2 = caller_locations(2..4)
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
end
it "can be called with a range whose end is negative" do
locations1 = caller_locations(0)
locations2 = caller_locations(2..-1)
locations3 = caller_locations(2..-2)
locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
locations1[2..-2].map(&:to_s).should == locations3.map(&:to_s)
end
it "must return nil if omitting more locations than available" do
caller_locations(100).should == nil
caller_locations(100..-1).should == nil
end
it "must return [] if omitting exactly the number of locations available" do
omit = caller_locations(0).length
caller_locations(omit).should == []
end
it 'returns the locations as Thread::Backtrace::Location instances' do it 'returns the locations as Thread::Backtrace::Location instances' do
locations = KernelSpecs::CallerLocationsTest.locations locations = KernelSpecs::CallerLocationsTest.locations
@ -29,4 +53,8 @@ describe 'Kernel#caller_locations' do
location.kind_of?(Thread::Backtrace::Location).should == true location.kind_of?(Thread::Backtrace::Location).should == true
end end
end end
it "must return the same locations when called with 1..-1 and when called with no arguments" do
caller_locations.map(&:to_s).should == caller_locations(1..-1).map(&:to_s)
end
end end

View file

@ -1,7 +1,6 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/sprintf' require_relative 'shared/sprintf'
require "stringio"
describe "Kernel#printf" do describe "Kernel#printf" do
it "is a private method" do it "is a private method" do
@ -32,8 +31,14 @@ describe "Kernel.printf" do
object.should_receive(:write).with("string") object.should_receive(:write).with("string")
Kernel.printf(object, "%s", "string") Kernel.printf(object, "%s", "string")
end end
end
describe "Kernel.printf" do
describe "formatting" do describe "formatting" do
before :each do
require "stringio"
end
context "io is specified" do context "io is specified" do
it_behaves_like :kernel_sprintf, -> format, *args { it_behaves_like :kernel_sprintf, -> format, *args {
io = StringIO.new io = StringIO.new
@ -45,7 +50,6 @@ describe "Kernel.printf" do
context "io is not specified" do context "io is not specified" do
it_behaves_like :kernel_sprintf, -> format, *args { it_behaves_like :kernel_sprintf, -> format, *args {
stdout = $stdout stdout = $stdout
begin begin
$stdout = io = StringIO.new $stdout = io = StringIO.new
Kernel.printf(format, *args) Kernel.printf(format, *args)

View file

@ -111,7 +111,7 @@ describe "Kernel#warn" do
-> { w.f4(obj, 2) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f2_call_lineno}: warning: to_s called|) -> { w.f4(obj, 2) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f2_call_lineno}: warning: to_s called|)
end end
it "does not prepend caller information if line number is too big" do it "does not prepend caller information if the uplevel argument is too large" do
w = KernelSpecs::WarnInNestedCall.new w = KernelSpecs::WarnInNestedCall.new
-> { w.f4("foo", 100) }.should output(nil, "warning: foo\n") -> { w.f4("foo", 100) }.should output(nil, "warning: foo\n")
end end

View file

@ -556,13 +556,10 @@ describe "Marshal.dump" do
end end
describe "when passed a StringIO" do describe "when passed a StringIO" do
it "should raise an error" do it "should raise an error" do
require "stringio" require "stringio"
-> { Marshal.dump(StringIO.new) }.should raise_error(TypeError) -> { Marshal.dump(StringIO.new) }.should raise_error(TypeError)
end end
end end
it "raises a TypeError if marshalling a Method instance" do it "raises a TypeError if marshalling a Method instance" do

View file

@ -1,6 +1,5 @@
# -*- encoding: binary -*- # -*- encoding: binary -*-
require_relative '../fixtures/marshal_data' require_relative '../fixtures/marshal_data'
require 'stringio'
describe :marshal_load, shared: true do describe :marshal_load, shared: true do
before :all do before :all do
@ -410,8 +409,9 @@ describe :marshal_load, shared: true do
end end
it "loads a string through StringIO stream" do it "loads a string through StringIO stream" do
obj = "This is a string which should be unmarshalled through StringIO stream!" require 'stringio'
Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj obj = "This is a string which should be unmarshalled through StringIO stream!"
Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj
end end
it "loads a string with an ivar" do it "loads a string with an ivar" do

View file

@ -81,6 +81,19 @@ describe "Range#bsearch" do
[1, 2].should include(result) [1, 2].should include(result)
end end
end end
it "returns nil for empty ranges" do
(0...0).bsearch { true }.should == nil
(0...0).bsearch { false }.should == nil
(0...0).bsearch { 1 }.should == nil
(0...0).bsearch { 0 }.should == nil
(0...0).bsearch { -1 }.should == nil
(4..2).bsearch { true }.should == nil
(4..2).bsearch { 1 }.should == nil
(4..2).bsearch { 0 }.should == nil
(4..2).bsearch { -1 }.should == nil
end
end end
context "with Float values" do context "with Float values" do
@ -94,13 +107,46 @@ describe "Range#bsearch" do
end end
it "returns minimum element if the block returns true for every element" do it "returns minimum element if the block returns true for every element" do
(-0.2..4.8).bsearch { |x| x < 4 }.should == -0.2 (-0.2..4.8).bsearch { |x| x < 5 }.should == -0.2
end end
it "returns the smallest element for which block returns true" do it "returns the smallest element for which block returns true" do
(0..4.2).bsearch { |x| x >= 2 }.should == 2 (0..4.2).bsearch { |x| x >= 2 }.should == 2
(-1.2..4.3).bsearch { |x| x >= 1 }.should == 1 (-1.2..4.3).bsearch { |x| x >= 1 }.should == 1
end end
it "returns a boundary element if appropriate" do
(1.0..3.0).bsearch { |x| x >= 3.0 }.should == 3.0
(1.0...3.0).bsearch { |x| x >= 3.0.prev_float }.should == 3.0.prev_float
(1.0..3.0).bsearch { |x| x >= 1.0 }.should == 1.0
(1.0...3.0).bsearch { |x| x >= 1.0 }.should == 1.0
end
it "works with infinity bounds" do
inf = Float::INFINITY
(0..inf).bsearch { |x| x == inf }.should == inf
(0...inf).bsearch { |x| x == inf }.should == nil
(-inf..0).bsearch { |x| x == -inf }.should == nil
(-inf...0).bsearch { |x| x == -inf }.should == nil
(inf..inf).bsearch { |x| true }.should == inf
(inf...inf).bsearch { |x| true }.should == nil
(-inf..-inf).bsearch { |x| true }.should == -inf
(-inf...-inf).bsearch { |x| true }.should == nil
(inf..0).bsearch { true }.should == nil
(inf...0).bsearch { true }.should == nil
(0..-inf).bsearch { true }.should == nil
(0...-inf).bsearch { true }.should == nil
(inf..-inf).bsearch { true }.should == nil
(inf...-inf).bsearch { true }.should == nil
(0..inf).bsearch { |x| x >= 3 }.should == 3.0
(0...inf).bsearch { |x| x >= 3 }.should == 3.0
(-inf..0).bsearch { |x| x >= -3 }.should == -3.0
(-inf...0).bsearch { |x| x >= -3 }.should == -3.0
(-inf..inf).bsearch { |x| x >= 3 }.should == 3.0
(-inf...inf).bsearch { |x| x >= 3 }.should == 3.0
(0..inf).bsearch { |x| x >= Float::MAX }.should == Float::MAX
(0...inf).bsearch { |x| x >= Float::MAX }.should == Float::MAX
end
end end
context "with a block returning negative, zero, positive numbers" do context "with a block returning negative, zero, positive numbers" do
@ -130,7 +176,157 @@ describe "Range#bsearch" do
it "returns an element at an index for which block returns 0" do it "returns an element at an index for which block returns 0" do
result = (0.1..4.9).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } result = (0.1..4.9).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
result.should >= 1 result.should >= 1
result.should <= 2 result.should <= 3
end
it "returns an element at an index for which block returns 0 (small numbers)" do
result = (0.1..0.3).bsearch { |x| x < 0.1 ? 1 : x > 0.3 ? -1 : 0 }
result.should >= 0.1
result.should <= 0.3
end
it "returns a boundary element if appropriate" do
(1.0..3.0).bsearch { |x| 3.0 - x }.should == 3.0
(1.0...3.0).bsearch { |x| 3.0.prev_float - x }.should == 3.0.prev_float
(1.0..3.0).bsearch { |x| 1.0 - x }.should == 1.0
(1.0...3.0).bsearch { |x| 1.0 - x }.should == 1.0
end
it "works with infinity bounds" do
inf = Float::INFINITY
(0..inf).bsearch { |x| x == inf ? 0 : -1 }.should == nil
(0...inf).bsearch { |x| x == inf ? 0 : -1 }.should == nil
(-inf...0).bsearch { |x| x == -inf ? 0 : 1 }.should == nil
(-inf..0).bsearch { |x| x == -inf ? 0 : 1 }.should == nil
(inf..inf).bsearch { 0 }.should == inf
(inf...inf).bsearch { 0 }.should == nil
(-inf..-inf).bsearch { 0 }.should == -inf
(-inf...-inf).bsearch { 0 }.should == nil
(inf..0).bsearch { 0 }.should == nil
(inf...0).bsearch { 0 }.should == nil
(0..-inf).bsearch { 0 }.should == nil
(0...-inf).bsearch { 0 }.should == nil
(inf..-inf).bsearch { 0 }.should == nil
(inf...-inf).bsearch { 0 }.should == nil
(-inf..inf).bsearch { |x| 3 - x }.should == 3.0
(-inf...inf).bsearch { |x| 3 - x }.should == 3.0
(0...inf).bsearch { |x| x >= Float::MAX ? 0 : 1 }.should == Float::MAX
end
end
end
ruby_version_is "2.6" do
context "with endless ranges and Integer values" do
context "with a block returning true or false" do
it "returns minimum element if the block returns true for every element" do
eval("(-2..)").bsearch { |x| true }.should == -2
end
it "returns the smallest element for which block returns true" do
eval("(0..)").bsearch { |x| x >= 2 }.should == 2
eval("(-1..)").bsearch { |x| x >= 1 }.should == 1
end
end
context "with a block returning negative, zero, positive numbers" do
it "returns nil if the block returns less than zero for every element" do
eval("(0..)").bsearch { |x| -1 }.should be_nil
end
it "returns nil if the block never returns zero" do
eval("(0..)").bsearch { |x| x > 5 ? -1 : 1 }.should be_nil
end
it "accepts -Float::INFINITY from the block" do
eval("(0..)").bsearch { |x| -Float::INFINITY }.should be_nil
end
it "returns an element at an index for which block returns 0.0" do
result = eval("(0..)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 }
result.should == 2
end
it "returns an element at an index for which block returns 0" do
result = eval("(0..)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
[1, 2].should include(result)
end
end
end
context "with endless ranges and Float values" do
context "with a block returning true or false" do
it "returns nil if the block returns false for every element" do
eval("(0.1..)").bsearch { |x| x < 0.0 }.should be_nil
eval("(0.1...)").bsearch { |x| x < 0.0 }.should be_nil
end
it "returns nil if the block returns nil for every element" do
eval("(-0.0..)").bsearch { |x| nil }.should be_nil
eval("(-0.0...)").bsearch { |x| nil }.should be_nil
end
it "returns minimum element if the block returns true for every element" do
eval("(-0.2..)").bsearch { |x| true }.should == -0.2
eval("(-0.2...)").bsearch { |x| true }.should == -0.2
end
it "returns the smallest element for which block returns true" do
eval("(0..)").bsearch { |x| x >= 2 }.should == 2
eval("(-1.2..)").bsearch { |x| x >= 1 }.should == 1
end
it "works with infinity bounds" do
inf = Float::INFINITY
eval("(inf..)").bsearch { |x| true }.should == inf
eval("(inf...)").bsearch { |x| true }.should == nil
eval("(-inf..)").bsearch { |x| true }.should == -inf
eval("(-inf...)").bsearch { |x| true }.should == -inf
end
end
context "with a block returning negative, zero, positive numbers" do
it "returns nil if the block returns less than zero for every element" do
eval("(-2.0..)").bsearch { |x| -1 }.should be_nil
eval("(-2.0...)").bsearch { |x| -1 }.should be_nil
end
it "returns nil if the block returns greater than zero for every element" do
eval("(0.3..)").bsearch { |x| 1 }.should be_nil
eval("(0.3...)").bsearch { |x| 1 }.should be_nil
end
it "returns nil if the block never returns zero" do
eval("(0.2..)").bsearch { |x| x < 2 ? 1 : -1 }.should be_nil
end
it "accepts (+/-)Float::INFINITY from the block" do
eval("(0.1..)").bsearch { |x| Float::INFINITY }.should be_nil
eval("(-5.0..)").bsearch { |x| -Float::INFINITY }.should be_nil
end
it "returns an element at an index for which block returns 0.0" do
result = eval("(0.0..)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 }
result.should == 2
end
it "returns an element at an index for which block returns 0" do
result = eval("(0.1..)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
result.should >= 1
result.should <= 3
end
it "works with infinity bounds" do
inf = Float::INFINITY
eval("(inf..)").bsearch { |x| 1 }.should == nil
eval("(inf...)").bsearch { |x| 1 }.should == nil
eval("(inf..)").bsearch { |x| x == inf ? 0 : 1 }.should == inf
eval("(inf...)").bsearch { |x| x == inf ? 0 : 1 }.should == nil
eval("(-inf..)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf
eval("(-inf...)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf
eval("(-inf..)").bsearch { |x| 3 - x }.should == 3
eval("(-inf...)").bsearch { |x| 3 - x }.should == 3
eval("(0.0...)").bsearch { 0 }.should != inf
end
end end
end end
end end

View file

@ -32,6 +32,28 @@ describe "Range#each" do
a.should == [x, y] a.should == [x, y]
end end
ruby_version_is "2.6" do
it "works with endless ranges" do
a = []
eval("(-2..)").each { |x| break if x > 2; a << x }
a.should == [-2, -1, 0, 1, 2]
a = []
eval("(-2...)").each { |x| break if x > 2; a << x }
a.should == [-2, -1, 0, 1, 2]
end
it "works with String endless ranges" do
a = []
eval("('A'..)").each { |x| break if x > "D"; a << x }
a.should == ["A", "B", "C", "D"]
a = []
eval("('A'...)").each { |x| break if x > "D"; a << x }
a.should == ["A", "B", "C", "D"]
end
end
it "raises a TypeError if the first element does not respond to #succ" do it "raises a TypeError if the first element does not respond to #succ" do
-> { (0.5..2.4).each { |i| i } }.should raise_error(TypeError) -> { (0.5..2.4).each { |i| i } }.should raise_error(TypeError)

View file

@ -7,4 +7,10 @@ describe "Range#==" do
it "returns true if the endpoints are ==" do it "returns true if the endpoints are ==" do
(0..1).should == (0..1.0) (0..1).should == (0..1.0)
end end
ruby_version_is "2.6" do
it "returns true if the endpoints are == for endless ranges" do
eval("(1.0..)").should == eval("(1.0..)")
end
end
end end

View file

@ -12,6 +12,13 @@ describe "Range#inspect" do
(0.5..2.4).inspect.should == "0.5..2.4" (0.5..2.4).inspect.should == "0.5..2.4"
end end
ruby_version_is "2.6" do
it "works for endless ranges" do
eval("(1..)").inspect.should == "1.."
eval("(0.1...)").inspect.should == "0.1..."
end
end
ruby_version_is ''...'2.7' do ruby_version_is ''...'2.7' do
it "returns a tainted string if either end is tainted" do it "returns a tainted string if either end is tainted" do
(("a".taint)..."c").inspect.tainted?.should be_true (("a".taint)..."c").inspect.tainted?.should be_true

View file

@ -46,4 +46,10 @@ describe "Range#last" do
it "raises a TypeError when passed a String" do it "raises a TypeError when passed a String" do
-> { (2..3).last("1") }.should raise_error(TypeError) -> { (2..3).last("1") }.should raise_error(TypeError)
end end
ruby_version_is "2.6" do
it "raises a RangeError when called on an endless range" do
-> { eval("(1..)").last }.should raise_error(RangeError)
end
end
end end

View file

@ -45,6 +45,12 @@ describe "Range#max" do
time_end = Time.now + 1.0 time_end = Time.now + 1.0
-> { (time_start...time_end).max }.should raise_error(TypeError) -> { (time_start...time_end).max }.should raise_error(TypeError)
end end
ruby_version_is "2.6" do
it "raises RangeError when called on an endless range" do
-> { eval("(1..)").max }.should raise_error(RangeError)
end
end
end end
describe "Range#max given a block" do describe "Range#max given a block" do

View file

@ -72,4 +72,11 @@ describe "Range#min given a block" do
('z'..'l').min {|x,y| x <=> y}.should be_nil ('z'..'l').min {|x,y| x <=> y}.should be_nil
(7...7).min {|x,y| x <=> y}.should be_nil (7...7).min {|x,y| x <=> y}.should be_nil
end end
ruby_version_is "2.6" do
it "returns the start point for endless ranges" do
eval("(1..)").min.should == 1
eval("(1.0...)").min.should == 1.0
end
end
end end

View file

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

View file

@ -42,4 +42,12 @@ describe :range_eql, shared: true do
b = RangeSpecs::MyRange.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5)) b = RangeSpecs::MyRange.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5))
a.send(@method, b).should == true a.send(@method, b).should == true
end end
ruby_version_is "2.6" do
it "works for endless Ranges" do
eval("(1..)").send(@method, eval("(1..)")).should == true
eval("(0.5...)").send(@method, eval("(0.5...)")).should == true
eval("(1..)").send(@method, eval("(1...)")).should == false
end
end
end end

View file

@ -24,6 +24,13 @@ describe "Range#size" do
(-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY
end end
ruby_version_is "2.6" do
it 'returns Float::INFINITY for endless ranges' do
eval("(1..)").size.should == Float::INFINITY
eval("(0.5...)").size.should == Float::INFINITY
end
end
it "returns nil if first and last are not Numeric" do it "returns nil if first and last are not Numeric" do
(:a..:z).size.should be_nil (:a..:z).size.should be_nil
('a'..'z').size.should be_nil ('a'..'z').size.should be_nil

View file

@ -274,6 +274,102 @@ describe "Range#step" do
end end
end end
ruby_version_is "2.6" do
describe "with an endless range" do
describe "and Integer values" do
it "yield Integer values incremented by 1 when not passed a step" do
eval("(-2..)").step { |x| break if x > 2; ScratchPad << x }
ScratchPad.recorded.should eql([-2, -1, 0, 1, 2])
ScratchPad.record []
eval("(-2...)").step { |x| break if x > 2; ScratchPad << x }
ScratchPad.recorded.should eql([-2, -1, 0, 1, 2])
end
it "yields Integer values incremented by an Integer step" do
eval("(-5..)").step(2) { |x| break if x > 3; ScratchPad << x }
ScratchPad.recorded.should eql([-5, -3, -1, 1, 3])
ScratchPad.record []
eval("(-5...)").step(2) { |x| break if x > 3; ScratchPad << x }
ScratchPad.recorded.should eql([-5, -3, -1, 1, 3])
end
it "yields Float values incremented by a Float step" do
eval("(-2..)").step(1.5) { |x| break if x > 1.0; ScratchPad << x }
ScratchPad.recorded.should eql([-2.0, -0.5, 1.0])\
ScratchPad.record []
eval("(-2..)").step(1.5) { |x| break if x > 1.0; ScratchPad << x }
ScratchPad.recorded.should eql([-2.0, -0.5, 1.0])
end
end
describe "and Float values" do
it "yields Float values incremented by 1 and less than end when not passed a step" do
eval("(-2.0..)").step { |x| break if x > 1.5; ScratchPad << x }
ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0])
ScratchPad.record []
eval("(-2.0...)").step { |x| break if x > 1.5; ScratchPad << x }
ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0])
end
it "yields Float values incremented by an Integer step" do
eval("(-5.0..)").step(2) { |x| break if x > 3.5; ScratchPad << x }
ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0])
ScratchPad.record []
eval("(-5.0...)").step(2) { |x| break if x > 3.5; ScratchPad << x }
ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0])
end
it "yields Float values incremented by a Float step" do
eval("(-1.0..)").step(0.5) { |x| break if x > 0.6; ScratchPad << x }
ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5])
ScratchPad.record []
eval("(-1.0...)").step(0.5) { |x| break if x > 0.6; ScratchPad << x }
ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5])
end
it "handles infinite values at the start" do
eval("(-Float::INFINITY..)").step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 }
ScratchPad.recorded.should eql([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY])
ScratchPad.record []
eval("(-Float::INFINITY...)").step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 }
ScratchPad.recorded.should eql([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY])
end
end
describe "and String values" do
it "yields String values incremented by #succ and less than or equal to end when not passed a step" do
eval("('A'..)").step { |x| break if x > "D"; ScratchPad << x }
ScratchPad.recorded.should == ["A", "B", "C", "D"]
ScratchPad.record []
eval("('A'...)").step { |x| break if x > "D"; ScratchPad << x }
ScratchPad.recorded.should == ["A", "B", "C", "D"]
end
it "yields String values incremented by #succ called Integer step times" do
eval("('A'..)").step(2) { |x| break if x > "F"; ScratchPad << x }
ScratchPad.recorded.should == ["A", "C", "E"]
ScratchPad.record []
eval("('A'...)").step(2) { |x| break if x > "F"; ScratchPad << x }
ScratchPad.recorded.should == ["A", "C", "E"]
end
it "raises a TypeError when passed a Float step" do
-> { eval("('A'..)").step(2.0) { } }.should raise_error(TypeError)
-> { eval("('A'...)").step(2.0) { } }.should raise_error(TypeError)
end
end
end
end
describe "when no block is given" do describe "when no block is given" do
describe "returned Enumerator" do describe "returned Enumerator" do
describe "size" do describe "size" do

View file

@ -19,4 +19,10 @@ describe "Range#to_a" do
it "works with Ranges of Symbols" do it "works with Ranges of Symbols" do
(:A..:z).to_a.size.should == 58 (:A..:z).to_a.size.should == 58
end end
ruby_version_is "2.6" do
it "throws an exception for endless ranges" do
-> { eval("(1..)").to_a }.should raise_error(RangeError)
end
end
end end

View file

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

View file

@ -93,6 +93,12 @@ describe "String#capitalize!" do
a.should == "Hello" a.should == "Hello"
end end
it "modifies self in place for non-ascii-compatible encodings" do
a = "heLLo".encode("utf-16le")
a.capitalize!
a.should == "Hello".encode("utf-16le")
end
describe "full Unicode case mapping" do describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do it "modifies self in place for all of Unicode with no option" do
a = "äöÜ" a = "äöÜ"
@ -106,6 +112,12 @@ describe "String#capitalize!" do
a.should == "Ss" a.should == "Ss"
end end
it "works for non-ascii-compatible encodings" do
a = "äöü".encode("utf-16le")
a.capitalize!
a.should == "Äöü".encode("utf-16le")
end
it "updates string metadata" do it "updates string metadata" do
capitalized = "ßeT" capitalized = "ßeT"
capitalized.capitalize! capitalized.capitalize!
@ -123,6 +135,12 @@ describe "String#capitalize!" do
a.capitalize!(:ascii) a.capitalize!(:ascii)
a.should == "ßet" a.should == "ßet"
end end
it "works for non-ascii-compatible encodings" do
a = "aBc".encode("utf-16le")
a.capitalize!(:ascii)
a.should == "Abc".encode("utf-16le")
end
end end
describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do

View file

@ -88,6 +88,12 @@ describe "String#downcase!" do
a.should == "hello" a.should == "hello"
end end
it "modifies self in place for non-ascii-compatible encodings" do
a = "HeLlO".encode("utf-16le")
a.downcase!
a.should == "hello".encode("utf-16le")
end
describe "full Unicode case mapping" do describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do it "modifies self in place for all of Unicode with no option" do
a = "ÄÖÜ" a = "ÄÖÜ"
@ -112,6 +118,12 @@ describe "String#downcase!" do
a.downcase!(:ascii) a.downcase!(:ascii)
a.should == "cÅr" a.should == "cÅr"
end end
it "works for non-ascii-compatible encodings" do
a = "ABC".encode("utf-16le")
a.downcase!(:ascii)
a.should == "abc".encode("utf-16le")
end
end end
describe "full Unicode case mapping adapted for Turkic languages" do describe "full Unicode case mapping adapted for Turkic languages" do

View file

@ -23,4 +23,17 @@ describe :string_length, shared: true do
str.force_encoding('BINARY').send(@method).should == 12 str.force_encoding('BINARY').send(@method).should == 12
end end
it "returns the correct length after force_encoding(BINARY)" do
utf8 = ""
ascii = "a"
concat = utf8 + ascii
concat.encoding.should == Encoding::UTF_8
concat.bytesize.should == 4
concat.size.should == 2
concat.force_encoding(Encoding::ASCII_8BIT)
concat.size.should == 4
end
end end

View file

@ -84,6 +84,24 @@ describe "String#split with String" do
$; = old_fs $; = old_fs
end end
end end
ruby_version_is "2.7" do
context "when $; is not nil" do
before do
suppress_warning do
@old_value, $; = $;, 'foobar'
end
end
after do
$; = @old_value
end
it "warns" do
-> { "".split }.should complain(/warning: \$; is set to non-nil value/)
end
end
end
end end
it "ignores leading and continuous whitespace when string is a single space" do it "ignores leading and continuous whitespace when string is a single space" do

View file

@ -86,6 +86,12 @@ describe "String#swapcase!" do
a.should == "CyBeR_pUnK11" a.should == "CyBeR_pUnK11"
end end
it "modifies self in place for non-ascii-compatible encodings" do
a = "cYbEr_PuNk11".encode("utf-16le")
a.swapcase!
a.should == "CyBeR_pUnK11".encode("utf-16le")
end
describe "full Unicode case mapping" do describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do it "modifies self in place for all of Unicode with no option" do
a = "äÖü" a = "äÖü"
@ -93,6 +99,12 @@ describe "String#swapcase!" do
a.should == "ÄöÜ" a.should == "ÄöÜ"
end end
it "works for non-ascii-compatible encodings" do
a = "äÖü".encode("utf-16le")
a.swapcase!
a.should == "ÄöÜ".encode("utf-16le")
end
it "updates string metadata" do it "updates string metadata" do
swapcased = "Aßet" swapcased = "Aßet"
swapcased.swapcase! swapcased.swapcase!
@ -110,6 +122,12 @@ describe "String#swapcase!" do
a.swapcase!(:ascii) a.swapcase!(:ascii)
a.should == "AßET" a.should == "AßET"
end end
it "works for non-ascii-compatible encodings" do
a = "aBc".encode("utf-16le")
a.swapcase!(:ascii)
a.should == "AbC".encode("utf-16le")
end
end end
describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do

View file

@ -85,6 +85,12 @@ describe "String#upcase!" do
a.should == "HELLO" a.should == "HELLO"
end end
it "modifies self in place for non-ascii-compatible encodings" do
a = "HeLlO".encode("utf-16le")
a.upcase!
a.should == "HELLO".encode("utf-16le")
end
describe "full Unicode case mapping" do describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do it "modifies self in place for all of Unicode with no option" do
a = "äöü" a = "äöü"
@ -92,6 +98,12 @@ describe "String#upcase!" do
a.should == "ÄÖÜ" a.should == "ÄÖÜ"
end end
it "works for non-ascii-compatible encodings" do
a = "äöü".encode("utf-16le")
a.upcase!
a.should == "ÄÖÜ".encode("utf-16le")
end
it "updates string metadata for self" do it "updates string metadata for self" do
upcased = "aßet" upcased = "aßet"
upcased.upcase! upcased.upcase!
@ -109,6 +121,12 @@ describe "String#upcase!" do
a.upcase!(:ascii) a.upcase!(:ascii)
a.should == "AßET" a.should == "AßET"
end end
it "works for non-ascii-compatible encodings" do
a = "abc".encode("utf-16le")
a.upcase!(:ascii)
a.should == "ABC".encode("utf-16le")
end
end end
describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do

View file

@ -0,0 +1,70 @@
require_relative '../../spec_helper'
ruby_version_is "2.7" do
describe "Struct#deconstruct_keys" do
it "returns a hash of attributes" do
struct = Struct.new(:x, :y)
s = struct.new(1, 2)
s.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
end
it "requires one argument" do
struct = Struct.new(:x)
obj = struct.new(1)
-> {
obj.deconstruct_keys
}.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
end
it "returns only specified keys" do
struct = Struct.new(:x, :y, :z)
s = struct.new(1, 2, 3)
s.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
s.deconstruct_keys([:x] ).should == {x: 1}
s.deconstruct_keys([] ).should == {}
end
it "accepts string attribute names" do
struct = Struct.new(:x, :y)
s = struct.new(1, 2)
s.deconstruct_keys(['x', 'y']).should == {'x' => 1, 'y' => 2}
end
it "accepts argument position number as well but returns them as keys" do
struct = Struct.new(:x, :y, :z)
s = struct.new(10, 20, 30)
s.deconstruct_keys([0, 1, 2]).should == {0 => 10, 1 => 20, 2 => 30}
s.deconstruct_keys([0, 1] ).should == {0 => 10, 1 => 20}
s.deconstruct_keys([0] ).should == {0 => 10}
end
it "ignores not existing attribute names" do
struct = Struct.new(:x, :y)
s = struct.new(1, 2)
s.deconstruct_keys([:a, :b, :c]).should == {}
end
it "accepts nil argument and return all the attributes" do
struct = Struct.new(:x, :y)
obj = struct.new(1, 2)
obj.deconstruct_keys(nil).should == {x: 1, y: 2}
end
it "raise TypeError if passed anything accept nil or array" do
struct = Struct.new(:x, :y)
s = struct.new(1, 2)
-> { s.deconstruct_keys('x') }.should raise_error(TypeError, /expected Array or nil/)
-> { s.deconstruct_keys(1) }.should raise_error(TypeError, /expected Array or nil/)
-> { s.deconstruct_keys(:x) }.should raise_error(TypeError, /expected Array or nil/)
-> { s.deconstruct_keys({}) }.should raise_error(TypeError, /expected Array or nil/)
end
end
end

View file

@ -0,0 +1,12 @@
require_relative '../../spec_helper'
ruby_version_is "2.7" do
describe "Struct#deconstruct" do
it "returns an array of attribute values" do
struct = Struct.new(:x, :y)
s = struct.new(1, 2)
s.deconstruct.should == [1, 2]
end
end
end

View file

@ -19,6 +19,22 @@ describe "Struct#hash" do
car.hash.should == similar_car.hash car.hash.should == similar_car.hash
end end
it "returns different hashes for structs with different values" do
s1 = StructClasses::Ruby.new('2.7.0', 'linux')
s2 = StructClasses::Ruby.new('2.7.0', 'macos')
s1.hash.should_not == s2.hash
end
ruby_version_is "2.5" do
it "returns different hashes for structs with different values when using keyword_init: true" do
key = :"1 non symbol member"
struct_class = Struct.new(key, keyword_init: true)
t1 = struct_class.new(key => 1)
t2 = struct_class.new(key => 2)
t1.hash.should_not == t2.hash
end
end
it "allows for overriding methods in an included module" do it "allows for overriding methods in an included module" do
mod = Module.new do mod = Module.new do
def hash def hash

View file

@ -19,6 +19,43 @@ describe "Thread#backtrace_locations" do
locations.each { |loc| loc.should be_an_instance_of(Thread::Backtrace::Location) } locations.each { |loc| loc.should be_an_instance_of(Thread::Backtrace::Location) }
end end
it "can be called with a number of locations to omit" do
locations1 = Thread.current.backtrace_locations
locations2 = Thread.current.backtrace_locations(2)
locations1[2..-1].length.should == locations2.length
locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
end
it "can be called with a maximum number of locations to return as second parameter" do
locations1 = Thread.current.backtrace_locations
locations2 = Thread.current.backtrace_locations(2, 3)
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
end
it "can be called with a range" do
locations1 = Thread.current.backtrace_locations
locations2 = Thread.current.backtrace_locations(2..4)
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
end
it "can be called with a range whose end is negative" do
locations1 = Thread.current.backtrace_locations
locations2 = Thread.current.backtrace_locations(2..-1)
locations3 = Thread.current.backtrace_locations(2..-2)
locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
locations1[2..-2].map(&:to_s).should == locations3.map(&:to_s)
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
end
it "returns [] if omitting exactly the number of locations available" do
omit = Thread.current.backtrace_locations.length
Thread.current.backtrace_locations(omit).should == []
end
it "without argument is the same as showing all locations with 0..-1" do it "without argument is the same as showing all locations with 0..-1" do
Thread.current.backtrace_locations.map(&:to_s).should == Thread.current.backtrace_locations(0..-1).map(&:to_s) Thread.current.backtrace_locations.map(&:to_s).should == Thread.current.backtrace_locations(0..-1).map(&:to_s)
end end

View file

@ -3,4 +3,19 @@ require_relative 'shared/inspect'
describe "Time#inspect" do describe "Time#inspect" do
it_behaves_like :inspect, :inspect it_behaves_like :inspect, :inspect
ruby_version_is "2.7" do
it "preserves milliseconds" 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"
t.inspect.should == "2007-11-01 15:25:00 8483885939586761/68719476736000000 UTC"
end
end
end end

View file

@ -255,4 +255,9 @@ describe "The alias keyword" do
code = '$a = 1; $b = 2; alias $b $a; p [$a, $b]; $b = 3; p [$a, $b]' code = '$a = 1; $b = 2; alias $b $a; p [$a, $b]; $b = 3; p [$a, $b]'
ruby_exe(code).should == "[1, 1]\n[3, 3]\n" ruby_exe(code).should == "[1, 1]\n[3, 3]\n"
end end
it "supports aliasing twice the same global variables" do
code = '$a = 1; alias $b $a; alias $b $a; p [$a, $b]'
ruby_exe(code).should == "[1, 1]\n"
end
end end

View file

@ -0,0 +1,15 @@
require_relative '../spec_helper'
describe "The comment" do
ruby_version_is "2.7" do
it "can be placed between fluent dot now" do
code = <<~CODE
10
# some comment
.to_s
CODE
eval(code).should == '10'
end
end
end

View file

@ -0,0 +1,87 @@
require_relative '../spec_helper'
ruby_version_is "2.7" do
describe "Numbered parameters" do
it "provides default parameters _1, _2, ... in a block" do
-> { _1 }.call("a").should == "a"
proc { _1 }.call("a").should == "a"
lambda { _1 }.call("a").should == "a"
["a"].map { _1 }.should == ["a"]
end
it "assigns nil to not passed parameters" do
proc { [_1, _2] }.call("a").should == ["a", nil]
proc { [_1, _2] }.call("a", "b").should == ["a", "b"]
end
it "supports variables _1-_9 only for the first 9 passed parameters" do
block = proc { [_1, _2, _3, _4, _5, _6, _7, _8, _9] }
result = block.call(1, 2, 3, 4, 5, 6, 7, 8, 9)
result.should == [1, 2, 3, 4, 5, 6, 7, 8, 9]
end
it "does not support more than 9 parameters" do
-> {
proc { [_10] }.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
}.should raise_error(NameError, /undefined local variable or method `_10'/)
end
it "can not be used in both outer and nested blocks at the same time" do
-> {
eval("-> { _1; -> { _2 } }")
}.should raise_error(SyntaxError, /numbered parameter is already used in.+ outer block here/m)
end
it "can be overwritten with local variable" do
suppress_warning do
eval <<~CODE
_1 = 0
proc { _1 }.call("a").should == 0
CODE
end
end
it "warns when numbered parameter is overriten with local variable" do
-> {
eval("_1 = 0")
}.should complain(/warning: `_1' is reserved for numbered parameter; consider another name/)
end
it "raises SyntaxError when block parameters are specified explicitly" do
-> { eval("-> () { _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
-> { eval("-> (x) { _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
-> { eval("proc { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
-> { eval("proc { |x| _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
-> { eval("lambda { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
-> { eval("lambda { |x| _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
-> { eval("['a'].map { || _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
-> { eval("['a'].map { |x| _1 }") }.should raise_error(SyntaxError, /ordinary parameter is defined/)
end
it "affects block arity" do
-> { _1 }.arity.should == 1
-> { _2 }.arity.should == 2
-> { _3 }.arity.should == 3
-> { _4 }.arity.should == 4
-> { _5 }.arity.should == 5
-> { _6 }.arity.should == 6
-> { _7 }.arity.should == 7
-> { _8 }.arity.should == 8
-> { _9 }.arity.should == 9
-> { _9 }.arity.should == 9
proc { _9 }.arity.should == 9
lambda { _9 }.arity.should == 9
end
it "does not work in methods" do
obj = Object.new
def obj.foo; _1 end
-> { obj.foo("a") }.should raise_error(ArgumentError, /wrong number of arguments/)
end
end
end

View file

@ -45,6 +45,10 @@ describe "A number literal" do
eval('-3r').should == Rational(-3, 1) eval('-3r').should == Rational(-3, 1)
end end
it "can be a float literal with trailing 'r' to represent a Rational" do
eval('0.0174532925199432957r').should == Rational(174532925199432957, 10000000000000000000)
end
it "can be an bignum literal with trailing 'r' to represent a Rational" do it "can be an bignum literal with trailing 'r' to represent a Rational" do
eval('1111111111111111111111111111111111111111111111r').should == Rational(1111111111111111111111111111111111111111111111, 1) eval('1111111111111111111111111111111111111111111111r').should == Rational(1111111111111111111111111111111111111111111111, 1)
eval('-1111111111111111111111111111111111111111111111r').should == Rational(-1111111111111111111111111111111111111111111111, 1) eval('-1111111111111111111111111111111111111111111111r').should == Rational(-1111111111111111111111111111111111111111111111, 1)

View file

@ -0,0 +1,966 @@
require_relative '../spec_helper'
ruby_version_is "2.7" do
describe "Pattern matching" do
# TODO: Remove excessive eval calls when support of previous version
# Ruby 2.6 will be dropped
before do
ScratchPad.record []
end
it "can be standalone in operator that deconstructs value" do
eval(<<-RUBY).should == [0, 1]
[0, 1] in [a, b]
[a, b]
RUBY
end
it "extends case expression with case/in construction" do
eval(<<~RUBY).should == :bar
case [0, 1]
in [0]
:foo
in [0, 1]
:bar
end
RUBY
end
it "allows using then operator" do
eval(<<~RUBY).should == :bar
case [0, 1]
in [0] then :foo
in [0, 1] then :bar
end
RUBY
end
it "warns about pattern matching is experimental feature" do
-> {
eval <<~RUBY
case 0
in 0
end
RUBY
}.should complain(/warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!/)
end
it "binds variables" do
eval(<<~RUBY).should == 1
case [0, 1]
in [0, a]
a
end
RUBY
end
it "cannot mix in and when operators" do
-> {
eval <<~RUBY
case []
when 1 == 1
in []
end
RUBY
}.should raise_error(SyntaxError, /syntax error, unexpected `in'/)
-> {
eval <<~RUBY
case []
in []
when 1 == 1
end
RUBY
}.should raise_error(SyntaxError, /syntax error, unexpected `when'/)
end
it "checks patterns until the first matching" do
eval(<<~RUBY).should == :bar
case [0, 1]
in [0]
:foo
in [0, 1]
:bar
in [0, 1]
:baz
end
RUBY
end
it "executes else clause if no pattern matches" do
eval(<<~RUBY).should == false
case [0, 1]
in [0]
true
else
false
end
RUBY
end
it "raises NoMatchingPatternError if no pattern matches and no else clause" do
-> {
eval <<~RUBY
case [0, 1]
in [0]
end
RUBY
}.should raise_error(NoMatchingPatternError, /\[0, 1\]/)
end
it "does not allow calculation or method calls in a pattern" do
-> {
eval <<~RUBY
case 0
in 1 + 1
true
end
RUBY
}.should raise_error(SyntaxError, /unexpected/)
end
describe "guards" do
it "supports if guard" do
eval(<<~RUBY).should == false
case 0
in 0 if false
true
else
false
end
RUBY
eval(<<~RUBY).should == true
case 0
in 0 if true
true
else
false
end
RUBY
end
it "supports unless guard" do
eval(<<~RUBY).should == false
case 0
in 0 unless true
true
else
false
end
RUBY
eval(<<~RUBY).should == true
case 0
in 0 unless false
true
else
false
end
RUBY
end
it "makes bound variables visible in guard" do
eval(<<~RUBY).should == true
case [0, 1]
in [a, 1] if a >= 0
true
end
RUBY
end
it "does not evaluate guard if pattern does not match" do
eval <<~RUBY
case 0
in 1 if (ScratchPad << :foo) || true
else
end
RUBY
ScratchPad.recorded.should == []
end
it "takes guards into account when there are several matching patterns" do
eval(<<~RUBY).should == :bar
case 0
in 0 if false
:foo
in 0 if true
:bar
end
RUBY
end
it "executes else clause if no guarded pattern matches" do
eval(<<~RUBY).should == false
case 0
in 0 if false
true
else
false
end
RUBY
end
it "raises NoMatchingPatternError if no guarded pattern matches and no else clause" do
-> {
eval <<~RUBY
case [0, 1]
in [0, 1] if false
end
RUBY
}.should raise_error(NoMatchingPatternError, /\[0, 1\]/)
end
end
describe "value pattern" do
it "matches an object such that pattern === object" do
eval(<<~RUBY).should == true
case 0
in 0
true
end
RUBY
eval(<<~RUBY).should == true
case 0
in (-1..1)
true
end
RUBY
eval(<<~RUBY).should == true
case 0
in Integer
true
end
RUBY
eval(<<~RUBY).should == true
case "0"
in /0/
true
end
RUBY
eval(<<~RUBY).should == true
case "0"
in ->(s) { s == "0" }
true
end
RUBY
end
it "allows string literal with interpolation" do
x = "x"
eval(<<~RUBY).should == true
case "x"
in "#{x + ""}"
true
end
RUBY
end
end
describe "variable pattern" do
it "matches a value and binds variable name to this value" do
eval(<<~RUBY).should == 0
case 0
in a
a
end
RUBY
end
it "makes bounded variable visible outside a case statement scope" do
eval(<<~RUBY).should == 0
case 0
in a
end
a
RUBY
end
it "create local variables even if a pattern doesn't match" do
eval(<<~RUBY).should == [0, nil, nil]
case 0
in a
in b
in c
end
[a, b, c]
RUBY
end
it "allow using _ name to drop values" do
eval(<<~RUBY).should == 0
case [0, 1]
in [a, _]
a
end
RUBY
end
it "supports using _ in a pattern several times" do
eval(<<~RUBY).should == 2
case [0, 1, 2]
in [0, _, _]
_
end
RUBY
end
it "supports using any name with _ at the beginning in a pattern several times" do
eval(<<~RUBY).should == 2
case [0, 1, 2]
in [0, _x, _x]
_x
end
RUBY
eval(<<~RUBY).should == 2
case {a: 0, b: 1, c: 2}
in {a: 0, b: _x, c: _x}
_x
end
RUBY
end
it "does not support using variable name (except _) several times" do
-> {
eval <<~RUBY
case [0]
in [a, a]
end
RUBY
}.should raise_error(SyntaxError, /duplicated variable name/)
end
it "supports existing variables in a pattern specified with ^ operator" do
a = 0
eval(<<~RUBY).should == true
case 0
in ^a
true
end
RUBY
end
it "allows applying ^ operator to bound variables" do
eval(<<~RUBY).should == 1
case [1, 1]
in [n, ^n]
n
end
RUBY
eval(<<~RUBY).should == false
case [1, 2]
in [n, ^n]
true
else
false
end
RUBY
end
it "requires bound variable to be specified in a pattern before ^ operator when it relies on a bound variable" do
-> {
eval <<~RUBY
case [1, 2]
in [^n, n]
true
else
false
end
RUBY
}.should raise_error(SyntaxError, /n: no such local variable/)
end
end
describe "alternative pattern" do
it "matches if any of patterns matches" do
eval(<<~RUBY).should == true
case 0
in 0 | 1 | 2
true
end
RUBY
end
it "does not support variable binding" do
-> {
eval <<~RUBY
case [0, 1]
in [0, 0] | [0, a]
end
RUBY
}.should raise_error(SyntaxError, /illegal variable in alternative pattern/)
end
end
describe "AS pattern" do
it "binds a variable to a value if pattern matches" do
eval(<<~RUBY).should == 0
case 0
in Integer => n
n
end
RUBY
end
it "can be used as a nested pattern" do
eval(<<~RUBY).should == [2, 3]
case [1, [2, 3]]
in [1, Array => ary]
ary
end
RUBY
end
end
describe "Array pattern" do
it "supports form Constant(pat, pat, ...)" do
eval(<<~RUBY).should == true
case [0, 1, 2]
in Array(0, 1, 2)
true
end
RUBY
end
it "supports form Constant[pat, pat, ...]" do
eval(<<~RUBY).should == true
case [0, 1, 2]
in Array[0, 1, 2]
true
end
RUBY
end
it "supports form [pat, pat, ...]" do
eval(<<~RUBY).should == true
case [0, 1, 2]
in [0, 1, 2]
true
end
RUBY
end
it "supports form pat, pat, ..." do
eval(<<~RUBY).should == true
case [0, 1, 2]
in 0, 1, 2
true
end
RUBY
eval(<<~RUBY).should == 1
case [0, 1, 2]
in 0, a, 2
a
end
RUBY
eval(<<~RUBY).should == [1, 2]
case [0, 1, 2]
in 0, *rest
rest
end
RUBY
end
it "matches an object with #deconstruct method which returns an array and each element in array matches element in pattern" do
obj = Object.new
def obj.deconstruct; [0, 1] end
eval(<<~RUBY).should == true
case obj
in [Integer, Integer]
true
end
RUBY
end
it "does not match object if Constant === object returns false" do
eval(<<~RUBY).should == false
case [0, 1, 2]
in String[0, 1, 2]
true
else
false
end
RUBY
end
it "does not match object without #deconstruct method" do
obj = Object.new
eval(<<~RUBY).should == false
case obj
in Object[]
true
else
false
end
RUBY
end
it "raises TypeError if #deconstruct method does not return array" do
obj = Object.new
def obj.deconstruct; "" end
-> {
eval <<~RUBY
case obj
in Object[]
else
end
RUBY
}.should raise_error(TypeError, /deconstruct must return Array/)
end
it "does not match object if elements of array returned by #deconstruct method does not match elements in pattern" do
obj = Object.new
def obj.deconstruct; [1] end
eval(<<~RUBY).should == false
case obj
in Object[0]
true
else
false
end
RUBY
end
it "binds variables" do
eval(<<~RUBY).should == [0, 1, 2]
case [0, 1, 2]
in [a, b, c]
[a, b, c]
end
RUBY
end
it "binds variable even if patter matches only partially" do
a = nil
eval(<<~RUBY).should == 0
case [0, 1, 2]
in [a, 1, 3]
else
end
a
RUBY
end
it "supports splat operator *rest" do
eval(<<~RUBY).should == [1, 2]
case [0, 1, 2]
in [0, *rest]
rest
end
RUBY
end
it "does not match partially by default" do
eval(<<~RUBY).should == false
case [0, 1, 2, 3]
in [1, 2]
true
else
false
end
RUBY
end
it "does match partially from the array beginning if list + , syntax used" do
eval(<<~RUBY).should == true
case [0, 1, 2, 3]
in [0, 1,]
true
end
RUBY
eval(<<~RUBY).should == true
case [0, 1, 2, 3]
in 0, 1,;
true
end
RUBY
end
it "matches [] with []" do
eval(<<~RUBY).should == true
case []
in []
true
end
RUBY
end
it "matches anything with *" do
eval(<<~RUBY).should == true
case [0, 1]
in *;
true
end
RUBY
end
end
describe "Hash pattern" do
it "supports form Constant(id: pat, id: pat, ...)" do
eval(<<~RUBY).should == true
case {a: 0, b: 1}
in Hash(a: 0, b: 1)
true
end
RUBY
end
it "supports form Constant[id: pat, id: pat, ...]" do
eval(<<~RUBY).should == true
case {a: 0, b: 1}
in Hash[a: 0, b: 1]
true
end
RUBY
end
it "supports form {id: pat, id: pat, ...}" do
eval(<<~RUBY).should == true
case {a: 0, b: 1}
in {a: 0, b: 1}
true
end
RUBY
end
it "supports form id: pat, id: pat, ..." do
eval(<<~RUBY).should == true
case {a: 0, b: 1}
in a: 0, b: 1
true
end
RUBY
eval(<<~RUBY).should == [0, 1]
case {a: 0, b: 1}
in a: a, b: b
[a, b]
end
RUBY
eval(<<~RUBY).should == { b: 1, c: 2 }
case {a: 0, b: 1, c: 2}
in a: 0, **rest
rest
end
RUBY
end
it "supports a: which means a: a" do
eval(<<~RUBY).should == [0, 1]
case {a: 0, b: 1}
in Hash(a:, b:)
[a, b]
end
RUBY
a = b = nil
eval(<<~RUBY).should == [0, 1]
case {a: 0, b: 1}
in Hash[a:, b:]
[a, b]
end
RUBY
a = b = nil
eval(<<~RUBY).should == [0, 1]
case {a: 0, b: 1}
in {a:, b:}
[a, b]
end
RUBY
a = nil
eval(<<~RUBY).should == [0, {b: 1, c: 2}]
case {a: 0, b: 1, c: 2}
in {a:, **rest}
[a, rest]
end
RUBY
a = b = nil
eval(<<~RUBY).should == [0, 1]
case {a: 0, b: 1}
in a:, b:
[a, b]
end
RUBY
end
it "can mix key (a:) and key-value (a: b) declarations" do
eval(<<~RUBY).should == [0, 1]
case {a: 0, b: 1}
in Hash(a:, b: x)
[a, x]
end
RUBY
end
it "supports 'string': key literal" do
eval(<<~RUBY).should == true
case {a: 0}
in {"a": 0}
true
end
RUBY
end
it "does not support non-symbol keys" do
-> {
eval <<~RUBY
case {a: 1}
in {"a" => 1}
end
RUBY
}.should raise_error(SyntaxError, /unexpected/)
end
it "does not support string interpolation in keys" do
x = "a"
-> {
eval <<~'RUBY'
case {a: 1}
in {"#{x}": 1}
end
RUBY
}.should raise_error(SyntaxError, /symbol literal with interpolation is not allowed/)
end
it "raise SyntaxError when keys duplicate in pattern" do
-> {
eval <<~RUBY
case {a: 1}
in {a: 1, b: 2, a: 3}
end
RUBY
}.should raise_error(SyntaxError, /duplicated key name/)
end
it "matches an object with #deconstruct_keys method which returns a Hash with equal keys and each value in Hash matches value in pattern" do
obj = Object.new
def obj.deconstruct_keys(*); {a: 1} end
eval(<<~RUBY).should == true
case obj
in {a: 1}
true
end
RUBY
end
it "does not match object if Constant === object returns false" do
eval(<<~RUBY).should == false
case {a: 1}
in String[a: 1]
true
else
false
end
RUBY
end
it "does not match object without #deconstruct_keys method" do
obj = Object.new
eval(<<~RUBY).should == false
case obj
in Object[a: 1]
true
else
false
end
RUBY
end
it "does not match object if #deconstruct_keys method does not return Hash" do
obj = Object.new
def obj.deconstruct_keys(*); "" end
-> {
eval <<~RUBY
case obj
in Object[a: 1]
end
RUBY
}.should raise_error(TypeError, /deconstruct_keys must return Hash/)
end
it "does not match object if #deconstruct_keys method returns Hash with non-symbol keys" do
obj = Object.new
def obj.deconstruct_keys(*); {"a" => 1} end
eval(<<~RUBY).should == false
case obj
in Object[a: 1]
true
else
false
end
RUBY
end
it "does not match object if elements of Hash returned by #deconstruct_keys method does not match values in pattern" do
obj = Object.new
def obj.deconstruct_keys(*); {a: 1} end
eval(<<~RUBY).should == false
case obj
in Object[a: 2]
true
else
false
end
RUBY
end
it "passes keys specified in pattern as arguments to #deconstruct_keys method" do
obj = Object.new
def obj.deconstruct_keys(*args)
ScratchPad << args
{a: 1, b: 2, c: 3}
end
eval <<~RUBY
case obj
in Object[a: 1, b: 2, c: 3]
end
RUBY
ScratchPad.recorded.should == [[[:a, :b, :c]]]
end
it "passes keys specified in pattern to #deconstruct_keys method if pattern contains double splat operator **" do
obj = Object.new
def obj.deconstruct_keys(*args)
ScratchPad << args
{a: 1, b: 2, c: 3}
end
eval <<~RUBY
case obj
in Object[a: 1, b: 2, **]
end
RUBY
ScratchPad.recorded.should == [[[:a, :b]]]
end
it "passes nil to #deconstruct_keys method if pattern contains double splat operator **rest" do
obj = Object.new
def obj.deconstruct_keys(*args)
ScratchPad << args
{a: 1, b: 2}
end
eval <<~RUBY
case obj
in Object[a: 1, **rest]
end
RUBY
ScratchPad.recorded.should == [[nil]]
end
it "binds variables" do
eval(<<~RUBY).should == [0, 1, 2]
case {a: 0, b: 1, c: 2}
in {a: x, b: y, c: z}
[x, y, z]
end
RUBY
end
it "binds variable even if pattern matches only partially" do
x = nil
eval(<<~RUBY).should == 0
case {a: 0, b: 1}
in {a: x, b: 2}
else
end
x
RUBY
end
it "supports double splat operator **rest" do
eval(<<~RUBY).should == {b: 1, c: 2}
case {a: 0, b: 1, c: 2}
in {a: 0, **rest}
rest
end
RUBY
end
it "treats **nil like there should not be any other keys in a matched Hash" do
eval(<<~RUBY).should == true
case {a: 1, b: 2}
in {a: 1, b: 2, **nil}
true
end
RUBY
eval(<<~RUBY).should == false
case {a: 1, b: 2}
in {a: 1, **nil}
true
else
false
end
RUBY
end
it "can match partially" do
eval(<<~RUBY).should == true
case {a: 1, b: 2}
in {a: 1}
true
end
RUBY
end
it "matches {} with {}" do
eval(<<~RUBY).should == true
case {}
in {}
true
end
RUBY
end
it "matches anything with **" do
eval(<<~RUBY).should == true
case {a: 1}
in **;
true
end
RUBY
end
end
end
end

View file

@ -398,6 +398,19 @@ describe "Predefined global $!" do
$!.should == nil $!.should == nil
end end
it "should be cleared when an exception is rescued even when a non-local return from block" do
[ 1 ].each do
begin
raise StandardError.new('err')
rescue => e
$!.should == e
return
end
end
$!.should == nil
end
it "should not be cleared when an exception is not rescued" do it "should not be cleared when an exception is not rescued" do
e = StandardError.new e = StandardError.new
begin begin
@ -633,6 +646,12 @@ describe "Predefined global $," do
it "raises TypeError if assigned a non-String" do it "raises TypeError if assigned a non-String" do
-> { $, = Object.new }.should raise_error(TypeError) -> { $, = Object.new }.should raise_error(TypeError)
end end
ruby_version_is "2.7" do
it "warns if assigned non-nil" do
-> { $, = "_" }.should complain(/warning: `\$,' is deprecated/)
end
end
end end
describe "Predefined global $." do describe "Predefined global $." do
@ -662,6 +681,18 @@ describe "Predefined global $." do
end end
end end
describe "Predefined global $;" do
after :each do
$; = nil
end
ruby_version_is "2.7" do
it "warns if assigned non-nil" do
-> { $; = "_" }.should complain(/warning: `\$;' is deprecated/)
end
end
end
describe "Predefined global $_" do describe "Predefined global $_" do
it "is set to the last line read by e.g. StringIO#gets" do it "is set to the last line read by e.g. StringIO#gets" do
stdin = StringIO.new("foo\nbar\n", "r") stdin = StringIO.new("foo\nbar\n", "r")

View file

@ -16,4 +16,11 @@ describe "Literal Ranges" do
eval("(1...)").should == Range.new(1, nil, true) eval("(1...)").should == Range.new(1, nil, true)
end end
end end
ruby_version_is "2.7" do
it "creates beginless ranges" do
eval("(..1)").should == Range.new(nil, 1)
eval("(...1)").should == Range.new(nil, 1, true)
end
end
end end

View file

@ -481,5 +481,15 @@ describe "The rescue keyword" do
a = raise(Exception) rescue 1 a = raise(Exception) rescue 1
}.should raise_error(Exception) }.should raise_error(Exception)
end end
ruby_version_is "2.7" do
it "rescues with multiple assignment" do
a, b = raise rescue [1, 2]
a.should == 1
b.should == 2
end
end
end end
end end

View file

@ -0,0 +1,14 @@
require_relative '../../spec_helper'
require 'English'
describe "English" do
it "aliases $! to $ERROR_INFO and $ERROR_INFO still returns an Exception with a backtrace" do
exception = (1 / 0 rescue $ERROR_INFO)
exception.should be_kind_of(Exception)
exception.backtrace.should be_kind_of(Array)
end
it "aliases $@ to $ERROR_POSITION and $ERROR_POSITION still returns a backtrace" do
(1 / 0 rescue $ERROR_POSITION).should be_kind_of(Array)
end
end

View file

@ -12,5 +12,6 @@ module MD5Constants
Digest = "\2473\267qw\276\364\343\345\320\304\350\313\314\217n" Digest = "\2473\267qw\276\364\343\345\320\304\350\313\314\217n"
BlankHexdigest = "d41d8cd98f00b204e9800998ecf8427e" BlankHexdigest = "d41d8cd98f00b204e9800998ecf8427e"
Hexdigest = "a733b77177bef4e3e5d0c4e8cbcc8f6e" Hexdigest = "a733b77177bef4e3e5d0c4e8cbcc8f6e"
Base64digest = "pzO3cXe+9OPl0MToy8yPbg=="
end end

View file

@ -12,6 +12,7 @@ module SHA1Constants
BlankDigest = "\3329\243\356^kK\r2U\277\357\225`\030\220\257\330\a\t" BlankDigest = "\3329\243\356^kK\r2U\277\357\225`\030\220\257\330\a\t"
Digest = "X!\255b\323\035\352\314a|q\344+\376\317\361V9\324\343" Digest = "X!\255b\323\035\352\314a|q\344+\376\317\361V9\324\343"
BlankHexdigest = "da39a3ee5e6b4b0d3255bfef95601890afd80709" BlankHexdigest = "da39a3ee5e6b4b0d3255bfef95601890afd80709"
Hexdigest = "e907d2ba21c6c74bc0efd76e44d11fb9bbb7a75e" Hexdigest = "5821ad62d31deacc617c71e42bfecff15639d4e3"
Base64digest = "WCGtYtMd6sxhfHHkK/7P8VY51OM="
end end

View file

@ -13,5 +13,6 @@ module SHA256Constants
Digest = "\230b\265\344_\337\357\337\242\004\314\311A\211jb\350\373\254\370\365M\230B\002\372\020j\as\270\376" Digest = "\230b\265\344_\337\357\337\242\004\314\311A\211jb\350\373\254\370\365M\230B\002\372\020j\as\270\376"
BlankHexdigest = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" BlankHexdigest = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
Hexdigest = "9862b5e45fdfefdfa204ccc941896a62e8fbacf8f54d984202fa106a0773b8fe" Hexdigest = "9862b5e45fdfefdfa204ccc941896a62e8fbacf8f54d984202fa106a0773b8fe"
Base64digest = "mGK15F/f79+iBMzJQYlqYuj7rPj1TZhCAvoQagdzuP4="
end end

View file

@ -14,5 +14,6 @@ module SHA384Constants
Digest = "B&\266:\314\216z\361!TD\001{`\355\323\320MW%\270\272\0034n\034\026g\a\217\"\333s\202\275\002Y*\217]\207u\f\034\244\231\266f" Digest = "B&\266:\314\216z\361!TD\001{`\355\323\320MW%\270\272\0034n\034\026g\a\217\"\333s\202\275\002Y*\217]\207u\f\034\244\231\266f"
BlankHexdigest = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" BlankHexdigest = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"
Hexdigest = "4226b63acc8e7af1215444017b60edd3d04d5725b8ba03346e1c1667078f22db7382bd02592a8f5d87750c1ca499b666" Hexdigest = "4226b63acc8e7af1215444017b60edd3d04d5725b8ba03346e1c1667078f22db7382bd02592a8f5d87750c1ca499b666"
Base64digest = "Qia2OsyOevEhVEQBe2Dt09BNVyW4ugM0bhwWZwePIttzgr0CWSqPXYd1DBykmbZm"
end end

View file

@ -13,5 +13,6 @@ module SHA512Constants
Digest = "\241\231\232\365\002z\241\331\242\310=\367F\272\004\326\331g\315n\251Q\222\250\374E\257\254=\325\225\003SM\350\244\234\220\233=\031\230A;\000\203\233\340\323t\333\271\222w\266\307\2678\344\255j\003\216\300" Digest = "\241\231\232\365\002z\241\331\242\310=\367F\272\004\326\331g\315n\251Q\222\250\374E\257\254=\325\225\003SM\350\244\234\220\233=\031\230A;\000\203\233\340\323t\333\271\222w\266\307\2678\344\255j\003\216\300"
BlankHexdigest = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" BlankHexdigest = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
Hexdigest = "a1999af5027aa1d9a2c83df746ba04d6d967cd6ea95192a8fc45afac3dd59503534de8a49c909b3d1998413b00839be0d374dbb99277b6c7b738e4ad6a038ec0" Hexdigest = "a1999af5027aa1d9a2c83df746ba04d6d967cd6ea95192a8fc45afac3dd59503534de8a49c909b3d1998413b00839be0d374dbb99277b6c7b738e4ad6a038ec0"
Base64digest = "oZma9QJ6odmiyD33RroE1tlnzW6pUZKo/EWvrD3VlQNTTeiknJCbPRmYQTsAg5vg03TbuZJ3tse3OOStagOOwA=="
end end

View file

@ -0,0 +1,63 @@
require_relative '../../spec_helper'
require_relative '../../library/digest/sha1/shared/constants'
require_relative '../../library/digest/sha256/shared/constants'
require_relative '../../library/digest/sha384/shared/constants'
require_relative '../../library/digest/sha512/shared/constants'
require 'openssl'
describe "OpenSSL::Digest" do
describe ".digest" do
it "returns a SHA1 digest" do
OpenSSL::Digest.digest('sha1', SHA1Constants::Contents).should == SHA1Constants::Digest
end
it "returns a SHA256 digest" do
OpenSSL::Digest.digest('sha256', SHA256Constants::Contents).should == SHA256Constants::Digest
end
it "returns a SHA384 digest" do
OpenSSL::Digest.digest('sha384', SHA384Constants::Contents).should == SHA384Constants::Digest
end
it "returns a SHA512 digest" do
OpenSSL::Digest.digest('sha512', SHA512Constants::Contents).should == SHA512Constants::Digest
end
end
describe ".hexdigest" do
it "returns a SHA1 hexdigest" do
OpenSSL::Digest.hexdigest('sha1', SHA1Constants::Contents).should == SHA1Constants::Hexdigest
end
it "returns a SHA256 hexdigest" do
OpenSSL::Digest.hexdigest('sha256', SHA256Constants::Contents).should == SHA256Constants::Hexdigest
end
it "returns a SHA384 hexdigest" do
OpenSSL::Digest.hexdigest('sha384', SHA384Constants::Contents).should == SHA384Constants::Hexdigest
end
it "returns a SHA512 hexdigest" do
OpenSSL::Digest.hexdigest('sha512', SHA512Constants::Contents).should == SHA512Constants::Hexdigest
end
end
describe ".base64digest" do
it "returns a SHA1 base64digest" do
OpenSSL::Digest.base64digest('sha1', SHA1Constants::Contents).should == SHA1Constants::Base64digest
end
it "returns a SHA256 base64digest" do
OpenSSL::Digest.base64digest('sha256', SHA256Constants::Contents).should == SHA256Constants::Base64digest
end
it "returns a SHA384 base64digest" do
OpenSSL::Digest.base64digest('sha384', SHA384Constants::Contents).should == SHA384Constants::Base64digest
end
it "returns a SHA512 base64digest" do
OpenSSL::Digest.base64digest('sha512', SHA512Constants::Contents).should == SHA512Constants::Base64digest
end
end
end

View file

@ -40,4 +40,3 @@ ruby_version_is ''...'2.8' do
end end
end end
end end

View file

@ -305,6 +305,13 @@ static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE meth
return rb_funcall_with_block(obj, SYM2ID(method), 0, NULL, block); return rb_funcall_with_block(obj, SYM2ID(method), 0, NULL, block);
} }
static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE method) {
return rb_funcall(obj, SYM2ID(method), 15,
INT2FIX(15), INT2FIX(14), INT2FIX(13), INT2FIX(12), INT2FIX(11),
INT2FIX(10), INT2FIX(9), INT2FIX(8), INT2FIX(7), INT2FIX(6),
INT2FIX(5), INT2FIX(4), INT2FIX(3), INT2FIX(2), INT2FIX(1));
}
void Init_kernel_spec(void) { void Init_kernel_spec(void) {
VALUE cls = rb_define_class("CApiKernelSpecs", rb_cObject); VALUE cls = rb_define_class("CApiKernelSpecs", rb_cObject);
rb_define_method(cls, "rb_block_given_p", kernel_spec_rb_block_given_p, 0); rb_define_method(cls, "rb_block_given_p", kernel_spec_rb_block_given_p, 0);
@ -342,6 +349,7 @@ void Init_kernel_spec(void) {
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_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_with_block", kernel_spec_rb_funcall_with_block, 3); rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3);
} }

View file

@ -345,6 +345,40 @@ static VALUE object_spec_rb_class_inherited_p(VALUE self, VALUE mod, VALUE arg)
return rb_class_inherited_p(mod, arg); return rb_class_inherited_p(mod, arg);
} }
static VALUE speced_allocator(VALUE klass) {
VALUE flags = 0;
VALUE instance;
if (rb_class_inherited_p(klass, rb_cString)) {
flags = T_STRING;
} else if (rb_class_inherited_p(klass, rb_cArray)) {
flags = T_ARRAY;
} else {
flags = T_OBJECT;
}
instance = rb_newobj_of(klass, flags);
rb_iv_set(instance, "@from_custom_allocator", Qtrue);
return instance;
}
static VALUE define_alloc_func(VALUE self, VALUE klass) {
rb_define_alloc_func(klass, speced_allocator);
return Qnil;
}
static VALUE undef_alloc_func(VALUE self, VALUE klass) {
rb_undef_alloc_func(klass);
return Qnil;
}
static VALUE speced_allocator_p(VALUE self, VALUE klass) {
rb_alloc_func_t allocator = rb_get_alloc_func(klass);
return (allocator == speced_allocator) ? Qtrue : Qfalse;
}
static VALUE custom_alloc_func_p(VALUE self, VALUE klass) {
rb_alloc_func_t allocator = rb_get_alloc_func(klass);
return allocator ? Qtrue : Qfalse;
}
void Init_object_spec(void) { void Init_object_spec(void) {
VALUE cls = rb_define_class("CApiObjectSpecs", rb_cObject); VALUE cls = rb_define_class("CApiObjectSpecs", rb_cObject);
@ -412,6 +446,10 @@ void Init_object_spec(void) {
rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2); rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2);
rb_define_method(cls, "rb_copy_generic_ivar", object_spec_rb_copy_generic_ivar, 2); rb_define_method(cls, "rb_copy_generic_ivar", object_spec_rb_copy_generic_ivar, 2);
rb_define_method(cls, "rb_free_generic_ivar", object_spec_rb_free_generic_ivar, 1); rb_define_method(cls, "rb_free_generic_ivar", object_spec_rb_free_generic_ivar, 1);
rb_define_method(cls, "rb_define_alloc_func", define_alloc_func, 1);
rb_define_method(cls, "rb_undef_alloc_func", undef_alloc_func, 1);
rb_define_method(cls, "speced_allocator?", speced_allocator_p, 1);
rb_define_method(cls, "custom_alloc_func?", custom_alloc_func_p, 1);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -260,7 +260,7 @@ describe "C-API Hash function" do
hash_code = @s.compute_a_hash_code(53) hash_code = @s.compute_a_hash_code(53)
hash_code.should be_an_instance_of(Integer) hash_code.should be_an_instance_of(Integer)
hash_code.should == @s.compute_a_hash_code(53) hash_code.should == @s.compute_a_hash_code(53)
@s.compute_a_hash_code(90) == @s.compute_a_hash_code(90) @s.compute_a_hash_code(90).should == @s.compute_a_hash_code(90)
end end
end end
end end

View file

@ -578,6 +578,20 @@ describe "C-API Kernel function" do
end end
end end
describe 'rb_funcall' do
before :each do
@obj = Object.new
class << @obj
def many_args(*args)
args
end
end
end
it "can call a public method with 10 arguments" do
@s.rb_funcall_many_args(@obj, :many_args).should == 15.downto(1).to_a
end
end
describe 'rb_funcall_with_block' do describe 'rb_funcall_with_block' do
before :each do before :each do
@obj = Object.new @obj = Object.new

View file

@ -110,6 +110,11 @@ describe "CApiObject" do
@o.rb_respond_to(ObjectTest.new, :foo).should == true @o.rb_respond_to(ObjectTest.new, :foo).should == true
@o.rb_respond_to(ObjectTest.new, :bar).should == false @o.rb_respond_to(ObjectTest.new, :bar).should == false
end end
it "can be used with primitives" do
@o.rb_respond_to(true, :object_id).should == true
@o.rb_respond_to(14, :succ).should == true
end
end end
describe "rb_obj_respond_to" do describe "rb_obj_respond_to" do
@ -887,4 +892,78 @@ describe "CApiObject" do
end end
end end
end end
describe "allocator accessors" do
describe "rb_define_alloc_func" do
it "sets up the allocator" do
klass = Class.new
@o.rb_define_alloc_func(klass)
obj = klass.allocate
obj.class.should.equal?(klass)
obj.should have_instance_variable(:@from_custom_allocator)
end
it "sets up the allocator for a subclass of String" do
klass = Class.new(String)
@o.rb_define_alloc_func(klass)
obj = klass.allocate
obj.class.should.equal?(klass)
obj.should have_instance_variable(:@from_custom_allocator)
obj.should == ""
end
it "sets up the allocator for a subclass of Array" do
klass = Class.new(Array)
@o.rb_define_alloc_func(klass)
obj = klass.allocate
obj.class.should.equal?(klass)
obj.should have_instance_variable(:@from_custom_allocator)
obj.should == []
end
end
describe "rb_get_alloc_func" do
it "gets the allocator that is defined directly on a class" do
klass = Class.new
@o.rb_define_alloc_func(klass)
@o.speced_allocator?(Object).should == false
@o.speced_allocator?(klass).should == true
end
it "gets the allocator that is inherited" do
parent = Class.new
@o.rb_define_alloc_func(parent)
klass = Class.new(parent)
@o.speced_allocator?(Object).should == false
@o.speced_allocator?(klass).should == true
end
end
describe "rb_undef_alloc_func" do
it "makes rb_get_alloc_func() return NULL for a class without a custom allocator" do
klass = Class.new
@o.rb_undef_alloc_func(klass)
@o.custom_alloc_func?(klass).should == false
end
it "undefs the allocator for the class" do
klass = Class.new
@o.rb_define_alloc_func(klass)
@o.speced_allocator?(klass).should == true
@o.rb_undef_alloc_func(klass)
@o.custom_alloc_func?(klass).should == false
end
it "undefs the allocator for a class that inherits a allocator" do
parent = Class.new
@o.rb_define_alloc_func(parent)
klass = Class.new(parent)
@o.speced_allocator?(klass).should == true
@o.rb_undef_alloc_func(klass)
@o.custom_alloc_func?(klass).should == false
@o.speced_allocator?(parent).should == true
end
end
end
end end