This commit is contained in:
Benoit Daloze 2023-02-27 21:02:44 +01:00
parent de60139053
commit 18b4def471
230 changed files with 1889 additions and 189 deletions

View File

@ -0,0 +1,13 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#all?" do
@value_to_return = -> _ { true }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :all?
it "ignores the block if there is an argument" do
-> {
['bar', 'foobar'].all?(/bar/) { false }.should == true
}.should complain(/given block not used/)
end
end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#any?" do describe "Array#any?" do
describe 'with no block given (a default block of { |x| x } is implicit)' do describe 'with no block given (a default block of { |x| x } is implicit)' do
@ -19,6 +20,9 @@ describe "Array#any?" do
end end
describe 'with a block given' do describe 'with a block given' do
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :any?
it 'is false if the array is empty' do it 'is false if the array is empty' do
empty_array = [] empty_array = []
empty_array.any? {|v| 1 == 1 }.should == false empty_array.any? {|v| 1 == 1 }.should == false
@ -34,4 +38,12 @@ describe "Array#any?" do
array_with_members.any? {|v| v == 42 }.should == false array_with_members.any? {|v| v == 42 }.should == false
end end
end end
describe 'when given a pattern argument' do
it "ignores the block if there is an argument" do
-> {
['bar', 'foobar'].any?(/bar/) { false }.should == true
}.should complain(/given block not used/)
end
end
end end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#count" do describe "Array#count" do
it "returns the number of elements" do it "returns the number of elements" do
@ -12,4 +13,14 @@ describe "Array#count" do
it "returns the number of element for which the block evaluates to true" do it "returns the number of element for which the block evaluates to true" do
[:a, :b, :c].count { |s| s != :b }.should == 2 [:a, :b, :c].count { |s| s != :b }.should == 2
end end
it "ignores the block if there is an argument" do
-> {
[:a, :b, :b, :c].count(:b) { |e| e.size > 10 }.should == 2
}.should complain(/given block not used/)
end
context "when a block argument given" do
it_behaves_like :array_iterable_and_tolerating_size_increasing, :count
end
end end

View File

@ -2,6 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize' require_relative 'shared/enumeratorize'
require_relative 'shared/delete_if' require_relative 'shared/delete_if'
require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized' require_relative '../enumerable/shared/enumeratorized'
describe "Array#delete_if" do describe "Array#delete_if" do
@ -47,6 +48,35 @@ describe "Array#delete_if" do
-> { ArraySpecs.empty_frozen_array.delete_if {} }.should raise_error(FrozenError) -> { ArraySpecs.empty_frozen_array.delete_if {} }.should raise_error(FrozenError)
end end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.delete_if { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "only removes elements for which the block returns true, keeping the element which raised an error." do
a = [1, 2, 3, 4]
begin
a.delete_if do |e|
case e
when 2 then true
when 3 then raise StandardError, 'Oops'
else false
end
end
rescue StandardError
end
a.should == [1, 3, 4]
end
it_behaves_like :enumeratorized_with_origin_size, :delete_if, [1,2,3] it_behaves_like :enumeratorized_with_origin_size, :delete_if, [1,2,3]
it_behaves_like :delete_if, :delete_if it_behaves_like :delete_if, :delete_if
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :delete_if
end end

View File

@ -1,7 +1,11 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#drop_while" do describe "Array#drop_while" do
@value_to_return = -> _ { true }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :drop_while
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
[1, 2, 3, 4].drop_while { |n| n < 4 }.should == [4] [1, 2, 3, 4].drop_while { |n| n < 4 }.should == [4]
end end

View File

@ -40,3 +40,19 @@ describe "Array#each_index" do
it_behaves_like :enumeratorize, :each_index it_behaves_like :enumeratorize, :each_index
it_behaves_like :enumeratorized_with_origin_size, :each_index, [1,2,3] it_behaves_like :enumeratorized_with_origin_size, :each_index, [1,2,3]
end end
describe "Array#each_index" do
it "tolerates increasing an array size during iteration" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.each_index do |index|
ScratchPad << index
array << i if i < 100
i += 1
end
ScratchPad.recorded.should == (0..102).to_a # element indices
end
end

View File

@ -1,6 +1,7 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize' require_relative 'shared/enumeratorize'
require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized' require_relative '../enumerable/shared/enumeratorized'
# Mutating the array while it is being iterated is discouraged as it can result in confusing behavior. # Mutating the array while it is being iterated is discouraged as it can result in confusing behavior.
@ -75,3 +76,7 @@ describe "Array#each" do
it_behaves_like :enumeratorize, :each it_behaves_like :enumeratorize, :each
it_behaves_like :enumeratorized_with_origin_size, :each, [1,2,3] it_behaves_like :enumeratorized_with_origin_size, :each, [1,2,3]
end end
describe "Array#each" do
it_behaves_like :array_iterable_and_tolerating_size_increasing, :each
end

View File

@ -72,6 +72,48 @@ describe "Array#fill" do
-> { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError) -> { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError)
-> { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError) -> { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError)
end end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.fill { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "only changes elements before error is raised, keeping the element which raised an error." do
a = [1, 2, 3, 4]
begin
a.fill do |i|
case i
when 0 then -1
when 1 then -2
when 2 then raise StandardError, 'Oops'
else 0
end
end
rescue StandardError
end
a.should == [-1, -2, 3, 4]
end
it "tolerates increasing an array size during iteration" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.fill do |index|
ScratchPad << index
array << i if i < 100
i++
index
end
ScratchPad.recorded.should == [0, 1, 2]
end
end end
describe "Array#fill with (filler, index, length)" do describe "Array#fill with (filler, index, length)" do

View File

@ -53,7 +53,9 @@ describe "Array#initialize with no arguments" do
end end
it "does not use the given block" do it "does not use the given block" do
->{ [1, 2, 3].send(:initialize) { raise } }.should_not raise_error -> {
-> { [1, 2, 3].send(:initialize) { raise } }.should_not raise_error
}.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
end end
end end

View File

@ -1,17 +1,66 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'Array#intersect?' do describe 'Array#intersect?' do
ruby_version_is '3.1' do # https://bugs.ruby-lang.org/issues/15198 ruby_version_is '3.1' do # https://bugs.ruby-lang.org/issues/15198
describe 'when at least one element in two Arrays is the same' do describe 'when at least one element in two Arrays is the same' do
it 'returns true' do it 'returns true' do
[1, 2].intersect?([2, 3]).should == true [1, 2].intersect?([2, 3, 4]).should == true
[2, 3, 4].intersect?([1, 2]).should == true
end end
end end
describe 'when there are no elements in common between two Arrays' do describe 'when there are no elements in common between two Arrays' do
it 'returns false' do it 'returns false' do
[1, 2].intersect?([3, 4]).should == false [0, 1, 2].intersect?([3, 4]).should == false
[3, 4].intersect?([0, 1, 2]).should == false
[3, 4].intersect?([]).should == false
[].intersect?([0, 1, 2]).should == false
end end
end 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].intersect?(obj).should == true
end
it "determines equivalence between elements in the sense of eql?" do
obj1 = mock('1')
obj2 = mock('2')
obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0)
obj1.stub!(:eql?).and_return(true)
obj2.stub!(:eql?).and_return(true)
[obj1].intersect?([obj2]).should == true
obj1 = mock('3')
obj2 = mock('4')
obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0)
obj1.stub!(:eql?).and_return(false)
obj2.stub!(:eql?).and_return(false)
[obj1].intersect?([obj2]).should == false
end
it "does not call to_ary on array subclasses" do
[5, 6].intersect?(ArraySpecs::ToAryArray[1, 2, 5, 6]).should == true
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].intersect?([x]).should == true
end
it "has semantic of !(a & b).empty?" do
[].intersect?([]).should == false
[nil].intersect?([nil]).should == true
end
end end
end end

View File

@ -26,7 +26,9 @@ describe "Array.new with no arguments" do
end end
it "does not use the given block" do it "does not use the given block" do
->{ Array.new { raise } }.should_not raise_error -> {
-> { Array.new { raise } }.should_not raise_error
}.should complain(/warning: given block not used/, verbose: true)
end end
end end

View File

@ -0,0 +1,13 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#none?" do
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :none?
it "ignores the block if there is an argument" do
-> {
['bar', 'foobar'].none?(/baz/) { true }.should == true
}.should complain(/given block not used/)
end
end

View File

@ -0,0 +1,13 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#one?" do
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :one?
it "ignores the block if there is an argument" do
-> {
['bar', 'foobar'].one?(/foo/) { false }.should == true
}.should complain(/given block not used/)
end
end

View File

@ -2,6 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize' require_relative 'shared/enumeratorize'
require_relative 'shared/delete_if' require_relative 'shared/delete_if'
require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized' require_relative '../enumerable/shared/enumeratorized'
describe "Array#reject" do describe "Array#reject" do
@ -47,6 +48,10 @@ describe "Array#reject" do
it_behaves_like :enumeratorized_with_origin_size, :reject, [1,2,3] it_behaves_like :enumeratorized_with_origin_size, :reject, [1,2,3]
end end
describe "Array#reject" do
it_behaves_like :array_iterable_and_tolerating_size_increasing, :reject
end
describe "Array#reject!" do describe "Array#reject!" do
it "removes elements for which block is true" do it "removes elements for which block is true" do
a = [3, 4, 5, 6, 7, 8, 9, 10, 11] a = [3, 4, 5, 6, 7, 8, 9, 10, 11]
@ -111,6 +116,11 @@ describe "Array#reject!" do
-> { ArraySpecs.empty_frozen_array.reject! {} }.should raise_error(FrozenError) -> { ArraySpecs.empty_frozen_array.reject! {} }.should raise_error(FrozenError)
end end
it "raises a FrozenError on a frozen array only during iteration if called without a block" do
enum = ArraySpecs.frozen_array.reject!
-> { enum.each {} }.should raise_error(FrozenError)
end
it "does not truncate the array is the block raises an exception" do it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3] a = [1, 2, 3]
begin begin
@ -141,3 +151,8 @@ describe "Array#reject!" do
it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3] it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3]
it_behaves_like :delete_if, :reject! it_behaves_like :delete_if, :reject!
end end
describe "Array#reject!" do
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :reject!
end

View File

@ -38,6 +38,20 @@ describe "Array#reverse_each" do
[1, 2, 3].reverse_each.size.should == 3 [1, 2, 3].reverse_each.size.should == 3
end end
it "tolerates increasing an array size during iteration" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.reverse_each do |e|
ScratchPad << e
array.prepend i if i < 100
i += 1
end
ScratchPad.recorded.should == [:c, :a, 1]
end
it_behaves_like :enumeratorize, :reverse_each it_behaves_like :enumeratorize, :reverse_each
it_behaves_like :enumeratorized_with_origin_size, :reverse_each, [1,2,3] it_behaves_like :enumeratorized_with_origin_size, :reverse_each, [1,2,3]
end end

View File

@ -68,6 +68,21 @@ describe "Array#rindex" do
seen.should == [3] seen.should == [3]
end end
it "tolerates increasing an array size during iteration" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.rindex do |e|
ScratchPad << e
array.prepend i if i < 100
i += 1
false
end
ScratchPad.recorded.should == [:c, :a, 1]
end
describe "given no argument and no block" do describe "given no argument and no block" do
it "produces an Enumerator" do it "produces an Enumerator" do
enum = [4, 2, 1, 5, 1, 3].rindex enum = [4, 2, 1, 5, 1, 3].rindex

View File

@ -1,4 +1,5 @@
require_relative '../../enumerable/shared/enumeratorized' require_relative '../../enumerable/shared/enumeratorized'
require_relative '../shared/iterable_and_tolerating_size_increasing'
describe :array_collect, shared: true do describe :array_collect, shared: true do
it "returns a copy of array with each element replaced by the value returned by block" do it "returns a copy of array with each element replaced by the value returned by block" do
@ -46,6 +47,8 @@ describe :array_collect, shared: true do
@object = [1, 2, 3, 4] @object = [1, 2, 3, 4]
end end
it_should_behave_like :enumeratorized_with_origin_size it_should_behave_like :enumeratorized_with_origin_size
it_should_behave_like :array_iterable_and_tolerating_size_increasing
end end
describe :array_collect_b, shared: true do describe :array_collect_b, shared: true do
@ -102,8 +105,37 @@ describe :array_collect_b, shared: true do
end end
end end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.send(@method) { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "only changes elements before error is raised, keeping the element which raised an error." do
a = [1, 2, 3, 4]
begin
a.send(@method) do |e|
case e
when 1 then -1
when 2 then -2
when 3 then raise StandardError, 'Oops'
else 0
end
end
rescue StandardError
end
a.should == [-1, -2, 3, 4]
end
before :all do before :all do
@object = [1, 2, 3, 4] @object = [1, 2, 3, 4]
end end
it_should_behave_like :enumeratorized_with_origin_size it_should_behave_like :enumeratorized_with_origin_size
it_should_behave_like :array_iterable_and_tolerating_size_increasing
end end

View File

@ -1,3 +1,5 @@
require_relative '../shared/iterable_and_tolerating_size_increasing'
describe :array_index, shared: true do describe :array_index, shared: true do
it "returns the index of the first element == to object" do it "returns the index of the first element == to object" do
x = mock('3') x = mock('3')
@ -34,4 +36,6 @@ describe :array_index, shared: true do
[].send(@method).should be_an_instance_of(Enumerator) [].send(@method).should be_an_instance_of(Enumerator)
end end
end end
it_should_behave_like :array_iterable_and_tolerating_size_increasing
end end

View File

@ -11,7 +11,8 @@ describe :array_intersection, shared: true do
end end
it "creates an array with elements in order they are first encountered" do 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] [ 1, 2, 3, 2, 5, 6, 7, 8 ].send(@method, [ 5, 2, 3, 4 ]).should == [2, 3, 5] # array > other
[ 5, 2, 3, 4 ].send(@method, [ 1, 2, 3, 2, 5, 6, 7, 8 ]).should == [5, 2, 3] # array < other
end end
it "does not modify the original Array" do it "does not modify the original Array" do

View File

@ -0,0 +1,25 @@
describe :array_iterable_and_tolerating_size_increasing, shared: true do
before do
@value_to_return ||= -> _ { nil }
end
it "tolerates increasing an array size during iteration" do
# The goal is to trigger potential reallocation of internal array storage, so we:
# - use elements of different types, starting with the less generic (Integer)
# - add reasonably big number of new elements (~ 100)
array = [1, 2, 3] # to test some methods we need several uniq elements
array_to_join = [:a, :b, :c] + (4..100).to_a
ScratchPad.record []
i = 0
array.send(@method) do |e|
ScratchPad << e
array << array_to_join[i] if i < array_to_join.size
i += 1
@value_to_return.call(e)
end
ScratchPad.recorded.should == [1, 2, 3] + array_to_join
end
end

View File

@ -1,4 +1,5 @@
require_relative '../../enumerable/shared/enumeratorized' require_relative '../../enumerable/shared/enumeratorized'
require_relative '../shared/iterable_and_tolerating_size_increasing'
describe :keep_if, shared: true do describe :keep_if, shared: true do
it "deletes elements for which the block returns a false value" do it "deletes elements for which the block returns a false value" do
@ -56,5 +57,39 @@ describe :keep_if, shared: true do
-> { @frozen.send(@method) { false } }.should raise_error(FrozenError) -> { @frozen.send(@method) { false } }.should raise_error(FrozenError)
end end
end end
it "raises a FrozenError on a frozen array only during iteration if called without a block" do
enum = @frozen.send(@method)
-> { enum.each {} }.should raise_error(FrozenError)
end
end end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.send(@method) { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "only changes elements before error is raised, keeping the element which raised an error." do
a = [1, 2, 3, 4]
begin
a.send(@method) do |e|
case e
when 2 then false
when 3 then raise StandardError, 'Oops'
else true
end
end
rescue StandardError
end
a.should == [1, 3, 4]
end
@value_to_return = -> _ { true }
it_should_behave_like :array_iterable_and_tolerating_size_increasing
end end

View File

@ -2,11 +2,14 @@ require_relative '../../../spec_helper'
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
require_relative '../shared/enumeratorize' require_relative '../shared/enumeratorize'
require_relative '../shared/keep_if' require_relative '../shared/keep_if'
require_relative '../shared/iterable_and_tolerating_size_increasing'
require_relative '../../enumerable/shared/enumeratorized' require_relative '../../enumerable/shared/enumeratorized'
describe :array_select, shared: true do describe :array_select, shared: true do
it_should_behave_like :enumeratorize it_should_behave_like :enumeratorize
it_should_behave_like :array_iterable_and_tolerating_size_increasing
before :each do before :each do
@object = [1,2,3] @object = [1,2,3]
end end

View File

@ -1,5 +1,6 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized' require_relative '../enumerable/shared/enumeratorized'
describe "Array#sort_by!" do describe "Array#sort_by!" do
@ -31,6 +32,11 @@ describe "Array#sort_by!" do
-> { ArraySpecs.empty_frozen_array.sort_by! {}}.should raise_error(FrozenError) -> { ArraySpecs.empty_frozen_array.sort_by! {}}.should raise_error(FrozenError)
end end
it "raises a FrozenError on a frozen array only during iteration if called without a block" do
enum = ArraySpecs.frozen_array.sort_by!
-> { enum.each {} }.should raise_error(FrozenError)
end
it "returns the specified value when it would break in the given block" do it "returns the specified value when it would break in the given block" do
[1, 2, 3].sort_by!{ break :a }.should == :a [1, 2, 3].sort_by!{ break :a }.should == :a
end end
@ -48,5 +54,32 @@ describe "Array#sort_by!" do
[1].sort_by!(&:to_s).should == [1] [1].sort_by!(&:to_s).should == [1]
end end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.sort_by! { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "doesn't change array if error is raised" do
a = [4, 3, 2, 1]
begin
a.sort_by! do |e|
raise StandardError, 'Oops' if e == 1
e
end
rescue StandardError
end
a.should == [4, 3, 2, 1]
end
it_behaves_like :enumeratorized_with_origin_size, :sort_by!, [1,2,3] it_behaves_like :enumeratorized_with_origin_size, :sort_by!, [1,2,3]
end end
describe "Array#sort_by!" do
it_behaves_like :array_iterable_and_tolerating_size_increasing, :sort_by!
end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#sum" do describe "Array#sum" do
it "returns the sum of elements" do it "returns the sum of elements" do
@ -69,3 +70,8 @@ describe "Array#sum" do
[b].sum(a).should == 42 [b].sum(a).should == 42
end end
end end
describe "Array#sum" do
@value_to_return = -> _ { 1 }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :sum
end

View File

@ -1,5 +1,6 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
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
@ -26,3 +27,8 @@ describe "Array#take_while" do
end end
end end
end end
describe "Array#take_while" do
@value_to_return = -> _ { true }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :take_while
end

View File

@ -1,5 +1,6 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#to_h" do describe "Array#to_h" do
it "converts empty array to empty hash" do it "converts empty array to empty hash" do
@ -77,3 +78,8 @@ describe "Array#to_h" do
end end
end end
end end
describe "Array#to_h" do
@value_to_return = -> e { [e, e.to_s] }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :to_h
end

View File

@ -1,5 +1,6 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#uniq" do describe "Array#uniq" do
it "returns an array with no duplicates" do it "returns an array with no duplicates" do
@ -131,6 +132,11 @@ describe "Array#uniq" do
end end
end end
describe "Array#uniq" do
@value_to_return = -> e { e }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :uniq
end
describe "Array#uniq!" do describe "Array#uniq!" do
it "modifies the array in place" do it "modifies the array in place" do
a = [ "a", "a", "b", "b", "c" ] a = [ "a", "a", "b", "b", "c" ]
@ -214,4 +220,32 @@ describe "Array#uniq!" do
a.uniq! a.uniq!
a.should == [x] a.should == [x]
end end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.send(@method) { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "doesn't change array if error is raised" do
a = [1, 1, 2, 2, 3, 3, 4, 4]
begin
a.send(@method) do |e|
raise StandardError, 'Oops' if e == 3
e
end
rescue StandardError
end
a.should == [1, 1, 2, 2, 3, 3, 4, 4]
end
end
describe "Array#uniq!" do
@value_to_return = -> e { e }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :uniq!
end end

View File

@ -15,8 +15,231 @@ module BasicObjectSpecs
include InstExec include InstExec
end end
module InstEvalCVar module InstEval
instance_eval { @@count = 2 } module CVar
module Get
class ReceiverScope
@@cvar = :value_defined_in_receiver_scope
end
class BlockDefinitionScope
@@cvar = :value_defined_in_block_definition_scope
def block
-> * { @@cvar }
end
end
class CallerScope
@@cvar = :value_defined_in_caller_scope
def get_class_variable_with_string(obj)
obj.instance_eval("@@cvar")
end
def get_class_variable_with_block(obj, block)
obj.instance_eval(&block)
end
end
class CallerWithoutCVarScope
def get_class_variable_with_string(obj)
obj.instance_eval("@@cvar")
end
end
ReceiverWithCVarDefinedInSingletonClass = Class.new.new.tap do |obj|
obj.singleton_class.class_variable_set(:@@cvar, :value_defined_in_receiver_singleton_class)
end
end
module Set
class ReceiverScope
end
class BlockDefinitionScope
def self.get_class_variable
@@cvar
end
def block_to_assign(value)
-> * { @@cvar = value }
end
end
class CallerScope
def self.get_class_variable
@@cvar
end
def set_class_variable_with_string(obj, value)
obj.instance_eval("@@cvar=#{value.inspect}")
end
def set_class_variable_with_block(obj, block)
obj.instance_eval(&block)
end
end
end
end
end
module InstEval
module Constants
module ConstantInReceiverSingletonClass
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
FOO = :Receiver
def initialize
self.singleton_class.const_set(:FOO, :singleton_class)
end
end
end
module CallerScope
FOO = :CallerScope
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
FOO = :Caller
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
module ConstantInReceiverClass
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
FOO = :Receiver
end
end
module CallerScope
FOO = :CallerScope
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
FOO = :Caller
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
module ConstantInCallerClass
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
# FOO is not declared in a receiver class
end
end
module CallerScope
FOO = :CallerScope
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
FOO = :Caller
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
module ConstantInCallerOuterScopes
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
# FOO is not declared in a receiver class
end
end
module CallerScope
FOO = :CallerScope
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
# FOO is not declared in a caller class
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
module ConstantInReceiverParentClass
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
# FOO is not declared in a receiver class
end
end
module CallerScope
# FOO is not declared in a caller outer scopes
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
# FOO is not declared in a caller class
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
end
end end
class InstEvalConst class InstEvalConst
@ -26,7 +249,6 @@ module BasicObjectSpecs
module InstEvalOuter module InstEvalOuter
module Inner module Inner
obj = InstEvalConst.new obj = InstEvalConst.new
X_BY_STR = obj.instance_eval("INST_EVAL_CONST_X") rescue nil
X_BY_BLOCK = obj.instance_eval { INST_EVAL_CONST_X } rescue nil X_BY_BLOCK = obj.instance_eval { INST_EVAL_CONST_X } rescue nil
end end
end end

View File

@ -89,6 +89,18 @@ describe "BasicObject#instance_eval" do
BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99 BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99
end end
it "raises TypeError for frozen objects when tries to set receiver's instance variables" do
-> { nil.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen NilClass: nil")
-> { true.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen TrueClass: true")
-> { false.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen FalseClass: false")
-> { 1.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen Integer: 1")
-> { :symbol.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen Symbol: :symbol")
obj = Object.new
obj.freeze
-> { obj.instance_eval { @foo = 42 } }.should raise_error(FrozenError)
end
it "treats block-local variables as local to the block" do it "treats block-local variables as local to the block" do
prc = instance_eval <<-CODE prc = instance_eval <<-CODE
proc do |x, prc| proc do |x, prc|
@ -105,11 +117,6 @@ describe "BasicObject#instance_eval" do
prc.call(false, prc).should == 1 prc.call(false, prc).should == 1
end end
it "sets class variables in the receiver" do
BasicObjectSpecs::InstEvalCVar.class_variables.should include(:@@count)
BasicObjectSpecs::InstEvalCVar.send(:class_variable_get, :@@count).should == 2
end
it "makes the receiver metaclass the scoped class when used with a string" do it "makes the receiver metaclass the scoped class when used with a string" do
obj = Object.new obj = Object.new
obj.instance_eval %{ obj.instance_eval %{
@ -119,8 +126,52 @@ describe "BasicObject#instance_eval" do
obj.singleton_class.const_get(:B).should be_an_instance_of(Class) obj.singleton_class.const_get(:B).should be_an_instance_of(Class)
end end
it "gets constants in the receiver if a string given" do describe "constants lookup when a String given" do
BasicObjectSpecs::InstEvalOuter::Inner::X_BY_STR.should == 2 it "looks in the receiver singleton class first" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverSingletonClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverSingletonClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :singleton_class
end
ruby_version_is ""..."3.1" do
it "looks in the caller scope next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :Caller
end
end
ruby_version_is "3.1" do
it "looks in the receiver class next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :Receiver
end
end
it "looks in the caller class next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInCallerClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInCallerClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :Caller
end
it "looks in the caller outer scopes next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInCallerOuterScopes::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInCallerOuterScopes::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :CallerScope
end
it "looks in the receiver class hierarchy next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverParentClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverParentClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :ReceiverParent
end
end end
it "doesn't get constants in the receiver if a block given" do it "doesn't get constants in the receiver if a block given" do
@ -136,17 +187,51 @@ describe "BasicObject#instance_eval" do
end.should raise_error(TypeError) end.should raise_error(TypeError)
end end
quarantine! do # Not clean, leaves cvars lying around to break other specs describe "class variables lookup" do
it "scopes class var accesses in the caller when called on an Integer" do it "gets class variables in the caller class when called with a String" do
# Integer can take instance vars receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
Integer.class_eval "@@__tmp_instance_eval_spec = 1" caller = BasicObjectSpecs::InstEval::CVar::Get::CallerScope.new
(defined? @@__tmp_instance_eval_spec).should be_nil
@@__tmp_instance_eval_spec = 2 caller.get_class_variable_with_string(receiver).should == :value_defined_in_caller_scope
1.instance_eval { @@__tmp_instance_eval_spec }.should == 2 end
Integer.__send__(:remove_class_variable, :@@__tmp_instance_eval_spec)
it "gets class variables in the block definition scope when called with a block" do
receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Get::CallerScope.new
block = BasicObjectSpecs::InstEval::CVar::Get::BlockDefinitionScope.new.block
caller.get_class_variable_with_block(receiver, block).should == :value_defined_in_block_definition_scope
end
it "sets class variables in the caller class when called with a String" do
receiver = BasicObjectSpecs::InstEval::CVar::Set::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Set::CallerScope.new
caller.set_class_variable_with_string(receiver, 1)
BasicObjectSpecs::InstEval::CVar::Set::CallerScope.get_class_variable.should == 1
end
it "sets class variables in the block definition scope when called with a block" do
receiver = BasicObjectSpecs::InstEval::CVar::Set::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Set::CallerScope.new
block = BasicObjectSpecs::InstEval::CVar::Set::BlockDefinitionScope.new.block_to_assign(1)
caller.set_class_variable_with_block(receiver, block)
BasicObjectSpecs::InstEval::CVar::Set::BlockDefinitionScope.get_class_variable.should == 1
end
it "does not have access to class variables in the receiver class when called with a String" do
receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Get::CallerWithoutCVarScope.new
-> { caller.get_class_variable_with_string(receiver) }.should raise_error(NameError, /uninitialized class variable @@cvar/)
end
it "does not have access to class variables in the receiver's singleton class when called with a String" do
receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverWithCVarDefinedInSingletonClass
caller = BasicObjectSpecs::InstEval::CVar::Get::CallerWithoutCVarScope.new
-> { caller.get_class_variable_with_string(receiver) }.should raise_error(NameError, /uninitialized class variable @@cvar/)
end
end end
end
it "raises a TypeError when defining methods on numerics" do it "raises a TypeError when defining methods on numerics" do
-> do -> do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/basicobject/method_missing' require_relative '../../shared/basicobject/method_missing'
describe "BasicObject#method_missing" do describe "BasicObject#method_missing" do

View File

@ -0,0 +1,26 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is "3.2" do
describe "Data.define" do
it "accepts no arguments" do
empty_data = Data.define
empty_data.members.should == []
end
it "accepts symbols" do
movie_with_symbol = Data.define(:title, :year)
movie_with_symbol.members.should == [:title, :year]
end
it "accepts strings" do
movie_with_string = Data.define("title", "year")
movie_with_string.members.should == [:title, :year]
end
it "accepts a mix of strings and symbols" do
blockbuster_movie = Data.define("title", :year, "genre")
blockbuster_movie.members.should == [:title, :year, :genre]
end
end
end

View File

@ -0,0 +1,5 @@
module DataSpecs
ruby_version_is "3.2" do
Measure = Data.define(:amount, :unit)
end
end

View File

@ -0,0 +1,58 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is "3.2" do
describe "Data#initialize" do
it "accepts positional arguments" do
data = DataSpecs::Measure.new(42, "km")
data.amount.should == 42
data.unit.should == "km"
end
it "accepts alternative positional arguments" do
data = DataSpecs::Measure[42, "km"]
data.amount.should == 42
data.unit.should == "km"
end
it "accepts keyword arguments" do
data = DataSpecs::Measure.new(amount: 42, unit: "km")
data.amount.should == 42
data.unit.should == "km"
end
it "accepts alternative keyword arguments" do
data = DataSpecs::Measure[amount: 42, unit: "km"]
data.amount.should == 42
data.unit.should == "km"
end
it "raises ArgumentError if no arguments are given" do
-> {
DataSpecs::Measure.new
}.should raise_error(ArgumentError) { |e|
e.message.should.include?("missing keywords: :amount, :unit")
}
end
it "raises ArgumentError if at least one argument is missing" do
-> {
DataSpecs::Measure.new(unit: "km")
}.should raise_error(ArgumentError) { |e|
e.message.should.include?("missing keyword: :amount")
}
end
it "raises ArgumentError if unknown keyword is given" do
-> {
DataSpecs::Measure.new(amount: 42, unit: "km", system: "metric")
}.should raise_error(ArgumentError) { |e|
e.message.should.include?("unknown keyword: :system")
}
end
end
end

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#destination_encoding" do describe "Encoding::InvalidByteSequenceError#destination_encoding" do

View File

@ -1,4 +1,5 @@
# -*- encoding: binary -*- # -*- encoding: binary -*-
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#error_bytes" do describe "Encoding::InvalidByteSequenceError#error_bytes" do

View File

@ -1,4 +1,5 @@
# -*- encoding: binary -*- # -*- encoding: binary -*-
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#readagain_bytes" do describe "Encoding::InvalidByteSequenceError#readagain_bytes" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding_name" do describe "Encoding::UndefinedConversionError#source_encoding_name" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#source_encoding" do describe "Encoding::InvalidByteSequenceError#source_encoding" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative 'shared/name' require_relative 'shared/name'
describe "Encoding#name" do describe "Encoding#name" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative 'shared/name' require_relative 'shared/name'
describe "Encoding#to_s" do describe "Encoding#to_s" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#destination_encoding_name" do describe "Encoding::UndefinedConversionError#destination_encoding_name" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#destination_encoding" do describe "Encoding::UndefinedConversionError#destination_encoding" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#error_char" do describe "Encoding::UndefinedConversionError#error_char" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding_name" do describe "Encoding::UndefinedConversionError#source_encoding_name" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding" do describe "Encoding::UndefinedConversionError#source_encoding" do

View File

@ -177,5 +177,11 @@ describe "Enumerable#all?" do
multi.all?(pattern).should == true multi.all?(pattern).should == true
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]] pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end end
it "ignores the block if there is an argument" do
-> {
EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).all?(String) { true }.should == false
}.should complain(/given block not used/)
end
end end
end end

View File

@ -190,5 +190,11 @@ describe "Enumerable#any?" do
multi.any?(pattern).should == false multi.any?(pattern).should == false
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]] pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end end
it "ignores the block if there is an argument" do
-> {
EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).any?(String) { true }.should == false
}.should complain(/given block not used/)
end
end end
end end

View File

@ -29,6 +29,11 @@ describe "Enumerable#chunk" do
result.should == [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]] result.should == [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]]
end end
it "returns a partitioned Array of values" do
e = EnumerableSpecs::Numerous.new(1,2,3)
e.chunk { |x| x > 2 }.map(&:last).should == [[1, 2], [3]]
end
it "returns elements for which the block returns :_alone in separate Arrays" do it "returns elements for which the block returns :_alone in separate Arrays" do
e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1) e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1)
result = e.chunk { |x| x < 2 && :_alone }.to_a result = e.chunk { |x| x < 2 && :_alone }.to_a

View File

@ -143,5 +143,11 @@ describe "Enumerable#none?" do
multi.none?(pattern).should == true multi.none?(pattern).should == true
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]] pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end end
it "ignores the block if there is an argument" do
-> {
EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).none?(String) { true }.should == true
}.should complain(/given block not used/)
end
end end
end end

View File

@ -83,7 +83,6 @@ describe "Enumerable#one?" do
end end
end end
describe 'when given a pattern argument' do describe 'when given a pattern argument' do
it "calls `===` on the pattern the return value " do it "calls `===` on the pattern the return value " do
pattern = EnumerableSpecs::Pattern.new { |x| x == 1 } pattern = EnumerableSpecs::Pattern.new { |x| x == 1 }
@ -145,5 +144,11 @@ describe "Enumerable#one?" do
multi.one?(pattern).should == false multi.one?(pattern).should == false
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]] pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end end
it "ignores the block if there is an argument" do
-> {
EnumerableSpecs::Numerous.new(1, 2, 3, 4, "5").one?(String) { false }.should == true
}.should complain(/given block not used/)
end
end end
end end

View File

@ -1,3 +1,5 @@
require_relative '../../array/shared/iterable_and_tolerating_size_increasing'
describe :enumerable_inject, shared: true do describe :enumerable_inject, shared: true do
it "with argument takes a block with an accumulator (with argument as initial value) and the current element. Value of block becomes new accumulator" do it "with argument takes a block with an accumulator (with argument as initial value) and the current element. Value of block becomes new accumulator" do
a = [] a = []
@ -17,8 +19,13 @@ describe :enumerable_inject, shared: true do
end end
it "ignores the block if two arguments" do it "ignores the block if two arguments" do
EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-) { raise "we never get here"}.should == 4 -> {
[].send(@method, 3, :+) { raise "we never get here"}.should == 3 EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-) { raise "we never get here"}.should == 4
}.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
-> {
[1, 2, 3].send(@method, 10, :-) { raise "we never get here"}.should == 4
}.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
end end
it "can take a symbol argument" do it "can take a symbol argument" do
@ -31,10 +38,10 @@ describe :enumerable_inject, shared: true do
a.should == [[2, 5], [5, 3], [3, 6], [6, 1], [1, 4]] a.should == [[2, 5], [5, 3], [3, 6], [6, 1], [1, 4]]
end end
it "gathers whole arrays as elements when each yields multiple" do it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMulti.new multi = EnumerableSpecs::YieldsMulti.new
multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]] multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
end end
it "with inject arguments(legacy rubycon)" do it "with inject arguments(legacy rubycon)" do
# with inject argument # with inject argument
@ -68,6 +75,22 @@ describe :enumerable_inject, shared: true do
EnumerableSpecs::EachDefiner.new().send(@method) {|acc,x| 999 }.should == nil EnumerableSpecs::EachDefiner.new().send(@method) {|acc,x| 999 }.should == nil
end end
it "tolerates increasing a collection size during iterating Array" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.send(@method, nil) do |_, e|
ScratchPad << e
array << i if i < 100
i += 1
end
actual = ScratchPad.recorded
expected = [:a, :b, :c] + (0..99).to_a
actual.sort_by(&:to_s).should == expected.sort_by(&:to_s)
end
ruby_bug '#18635', ''...'3.2' do ruby_bug '#18635', ''...'3.2' do
it "raises an ArgumentError when no parameters or block is given" do it "raises an ArgumentError when no parameters or block is given" do
-> { [1,2].send(@method) }.should raise_error(ArgumentError) -> { [1,2].send(@method) }.should raise_error(ArgumentError)

View File

@ -11,4 +11,8 @@ describe "Enumerator::Chain#inspect" do
obj.should_receive(:inspect).and_return('some desc') obj.should_receive(:inspect).and_return('some desc')
Enumerator::Chain.new(obj).inspect.should == "#<Enumerator::Chain: [some desc]>" Enumerator::Chain.new(obj).inspect.should == "#<Enumerator::Chain: [some desc]>"
end end
it "returns a not initialized representation if #initialized is not called yet" do
Enumerator::Chain.allocate.inspect.should == "#<Enumerator::Chain: uninitialized>"
end
end end

View File

@ -14,4 +14,9 @@ describe "Enumerator#inspect" do
(1..3).each.each_slice(2).inspect.should == "#<Enumerator: #<Enumerator: 1..3:each>:each_slice(2)>" (1..3).each.each_slice(2).inspect.should == "#<Enumerator: #<Enumerator: 1..3:each>:each_slice(2)>"
end end
end end
it "returns a not initialized representation if #initialized is not called yet" do
Enumerator.allocate.inspect.should == "#<Enumerator: uninitialized>"
Enumerator::Lazy.allocate.inspect.should == "#<Enumerator::Lazy: uninitialized>"
end
end end

View File

@ -1,4 +1,5 @@
require_relative '../../../spec_helper' require_relative '../../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is '3.1' do ruby_version_is '3.1' do
describe "Enumerator::Lazy#compact" do describe "Enumerator::Lazy#compact" do
@ -7,5 +8,9 @@ ruby_version_is '3.1' do
arr.should be_an_instance_of(Enumerator::Lazy) arr.should be_an_instance_of(Enumerator::Lazy)
arr.force.should == [1, 3, false, 5] arr.force.should == [1, 3, false, 5]
end end
it "sets #size to nil" do
Enumerator::Lazy.new(Object.new, 100) {}.compact.size.should == nil
end
end end
end end

View File

@ -0,0 +1,22 @@
ready = false
t = Thread.new do
f = Fiber.new do
begin
Fiber.yield
ensure
STDERR.puts "suspended fiber ensure"
end
end
f.resume
begin
ready = true
sleep
ensure
STDERR.puts "current fiber ensure"
end
end
Thread.pass until ready && t.stop?
# let the program end, it's the same as #exit or an exception for this behavior

View File

@ -0,0 +1,25 @@
ready = false
t = Thread.new do
f = Fiber.new do
begin
Fiber.yield
ensure
STDERR.puts "suspended fiber ensure"
end
end
f.resume
f2 = Fiber.new do
begin
ready = true
sleep
ensure
STDERR.puts "current fiber ensure"
end
end
f2.resume
end
Thread.pass until ready && t.stop?
# let the program end, it's the same as #exit or an exception for this behavior

View File

@ -42,4 +42,16 @@ describe "An Exception reaching the top level" do
EOS EOS
end end
end end
describe "kills all threads and fibers, ensure clauses are only run for threads current fibers, not for suspended fibers" do
it "with ensure on the root fiber" do
file = fixture(__FILE__, "thread_fiber_ensure.rb")
ruby_exe(file, args: "2>&1", exit_status: 0).should == "current fiber ensure\n"
end
it "with ensure on non-root fiber" do
file = fixture(__FILE__, "thread_fiber_ensure_non_root_fiber.rb")
ruby_exe(file, args: "2>&1", exit_status: 0).should == "current fiber ensure\n"
end
end
end end

View File

@ -78,6 +78,22 @@ describe "File.new" do
File.should.exist?(@file) File.should.exist?(@file)
end end
it "returns a new read-only File when mode is not specified" do
@fh = File.new(@file)
-> { @fh.puts("test") }.should raise_error(IOError)
@fh.read.should == ""
File.should.exist?(@file)
end
it "returns a new read-only File when mode is not specified but flags option is present" do
@fh = File.new(@file, flags: File::CREAT)
-> { @fh.puts("test") }.should raise_error(IOError)
@fh.read.should == ""
File.should.exist?(@file)
end
it "creates a new file when use File::EXCL mode" do it "creates a new file when use File::EXCL mode" do
@fh = File.new(@file, File::EXCL) @fh = File.new(@file, File::EXCL)
@fh.should be_kind_of(File) @fh.should be_kind_of(File)
@ -112,13 +128,32 @@ describe "File.new" do
File.should.exist?(@file) File.should.exist?(@file)
end end
it "creates a new file when use File::WRONLY|File::TRUNC mode" do it "creates a new file when use File::WRONLY|File::TRUNC mode" do
@fh = File.new(@file, File::WRONLY|File::TRUNC) @fh = File.new(@file, File::WRONLY|File::TRUNC)
@fh.should be_kind_of(File) @fh.should be_kind_of(File)
File.should.exist?(@file) File.should.exist?(@file)
end end
it "returns a new read-only File when use File::RDONLY|File::CREAT mode" do
@fh = File.new(@file, File::RDONLY|File::CREAT)
@fh.should be_kind_of(File)
File.should.exist?(@file)
# it's read-only
-> { @fh.puts("test") }.should raise_error(IOError)
@fh.read.should == ""
end
it "returns a new read-only File when use File::CREAT mode" do
@fh = File.new(@file, File::CREAT)
@fh.should be_kind_of(File)
File.should.exist?(@file)
# it's read-only
-> { @fh.puts("test") }.should raise_error(IOError)
@fh.read.should == ""
end
it "coerces filename using to_str" do it "coerces filename using to_str" do
name = mock("file") name = mock("file")
name.should_receive(:to_str).and_return(@file) name.should_receive(:to_str).and_return(@file)
@ -133,6 +168,28 @@ describe "File.new" do
File.should.exist?(@file) File.should.exist?(@file)
end end
ruby_version_is "3.0" do
it "accepts options as a keyword argument" do
@fh = File.new(@file, 'w', 0755, flags: @flags)
@fh.should be_kind_of(File)
@fh.close
-> {
@fh = File.new(@file, 'w', 0755, {flags: @flags})
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
end
it "bitwise-ORs mode and flags option" do
-> {
@fh = File.new(@file, 'w', flags: File::EXCL)
}.should raise_error(Errno::EEXIST, /File exists/)
-> {
@fh = File.new(@file, mode: 'w', flags: File::EXCL)
}.should raise_error(Errno::EEXIST, /File exists/)
end
it "raises a TypeError if the first parameter can't be coerced to a string" do it "raises a TypeError if the first parameter can't be coerced to a string" do
-> { File.new(true) }.should raise_error(TypeError) -> { File.new(true) }.should raise_error(TypeError)
-> { File.new(false) }.should raise_error(TypeError) -> { File.new(false) }.should raise_error(TypeError)

View File

@ -565,6 +565,17 @@ describe "File.open" do
File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::BINARY} File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::BINARY}
end end
ruby_version_is "3.0" do
it "accepts options as a keyword argument" do
@fh = File.open(@file, 'w', 0755, flags: File::CREAT)
@fh.should be_an_instance_of(File)
-> {
File.open(@file, 'w', 0755, {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
end
it "uses the second argument as an options Hash" do it "uses the second argument as an options Hash" do
@fh = File.open(@file, mode: "r") @fh = File.open(@file, mode: "r")
@fh.should be_an_instance_of(File) @fh.should be_an_instance_of(File)

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative 'shared/abs' require_relative 'shared/abs'
describe "Float#magnitude" do describe "Float#magnitude" do

View File

@ -27,6 +27,19 @@ describe "IO#initialize" do
@io.fileno.should == fd @io.fileno.should == fd
end end
ruby_version_is "3.0" do
it "accepts options as keyword arguments" do
fd = new_fd @name, "w:utf-8"
@io.send(:initialize, fd, "w", flags: File::CREAT)
@io.fileno.should == fd
-> {
@io.send(:initialize, fd, "w", {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
end
end
it "raises a TypeError when passed an IO" do it "raises a TypeError when passed an IO" do
-> { @io.send :initialize, STDOUT, 'w' }.should raise_error(TypeError) -> { @io.send :initialize, STDOUT, 'w' }.should raise_error(TypeError)
end end

View File

@ -23,6 +23,17 @@ describe "IO.read" do
IO.read(p) IO.read(p)
end end
ruby_version_is "3.0" do
# https://bugs.ruby-lang.org/issues/19354
it "accepts options as keyword arguments" do
IO.read(@fname, 3, 0, mode: "r+").should == @contents[0, 3]
-> {
IO.read(@fname, 3, 0, {mode: "r+"})
}.should raise_error(ArgumentError, /wrong number of arguments/)
end
end
it "accepts an empty options Hash" do it "accepts an empty options Hash" do
IO.read(@fname, **{}).should == @contents IO.read(@fname, **{}).should == @contents
end end
@ -55,6 +66,31 @@ describe "IO.read" do
IO.read(@fname, mode: "a+").should == @contents IO.read(@fname, mode: "a+").should == @contents
end end
platform_is_not :windows do
it "uses an :open_args option" do
string = IO.read(@fname, nil, 0, open_args: ["r", nil, {encoding: Encoding::US_ASCII}])
string.encoding.should == Encoding::US_ASCII
string = IO.read(@fname, nil, 0, open_args: ["r", nil, {}])
string.encoding.should == Encoding::UTF_8
end
end
it "disregards other options if :open_args is given" do
string = IO.read(@fname,mode: "w", encoding: Encoding::UTF_32LE, open_args: ["r", encoding: Encoding::UTF_8])
string.encoding.should == Encoding::UTF_8
end
it "doesn't require mode to be specified in :open_args" do
string = IO.read(@fname, nil, 0, open_args: [{encoding: Encoding::US_ASCII}])
string.encoding.should == Encoding::US_ASCII
end
it "doesn't require mode to be specified in :open_args even if flags option passed" do
string = IO.read(@fname, nil, 0, open_args: [{encoding: Encoding::US_ASCII, flags: File::CREAT}])
string.encoding.should == Encoding::US_ASCII
end
it "treats second nil argument as no length limit" do it "treats second nil argument as no length limit" do
IO.read(@fname, nil).should == @contents IO.read(@fname, nil).should == @contents
IO.read(@fname, nil, 5).should == IO.read(@fname, @contents.length, 5) IO.read(@fname, nil, 5).should == IO.read(@fname, @contents.length, 5)

View File

@ -127,6 +127,12 @@ describe "IO#readlines" do
end end
end end
end end
describe "when passed arbitrary keyword argument" do
it "tolerates it" do
@io.readlines(chomp: true, foo: :bar).should == IOSpecs.lines_without_newline_characters
end
end
end end
describe "IO#readlines" do describe "IO#readlines" do

View File

@ -21,6 +21,16 @@ describe :io_binwrite, shared: true do
IO.send(@method, @filename, "abcde").should == 5 IO.send(@method, @filename, "abcde").should == 5
end end
ruby_version_is "3.0" do
it "accepts options as a keyword argument" do
IO.send(@method, @filename, "hi", 0, flags: File::CREAT).should == 2
-> {
IO.send(@method, @filename, "hi", 0, {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 2..3)")
end
end
it "creates a file if missing" do it "creates a file if missing" do
fn = @filename + "xxx" fn = @filename + "xxx"
begin begin
@ -67,6 +77,11 @@ describe :io_binwrite, shared: true do
File.read(@filename).should == "\0\0foo" File.read(@filename).should == "\0\0foo"
end end
it "accepts a :flags option without :mode one" do
IO.send(@method, @filename, "hello, world!", flags: File::CREAT)
File.read(@filename).should == "hello, world!"
end
it "raises an error if readonly mode is specified" do it "raises an error if readonly mode is specified" do
-> { IO.send(@method, @filename, "abcde", mode: "r") }.should raise_error(IOError) -> { IO.send(@method, @filename, "abcde", mode: "r") }.should raise_error(IOError)
end end

View File

@ -64,6 +64,17 @@ describe :io_new, shared: true do
@io.should be_an_instance_of(IO) @io.should be_an_instance_of(IO)
end end
ruby_version_is "3.0" do
it "accepts options as keyword arguments" do
@io = IO.send(@method, @fd, "w", flags: File::CREAT)
@io.write("foo").should == 3
-> {
IO.send(@method, @fd, "w", {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
end
end
it "accepts a :mode option" do it "accepts a :mode option" do
@io = IO.send(@method, @fd, mode: "w") @io = IO.send(@method, @fd, mode: "w")
@io.write("foo").should == 3 @io.write("foo").should == 3

View File

@ -24,10 +24,6 @@ describe "IO#write on a file" do
-> { @readonly_file.write("") }.should_not raise_error -> { @readonly_file.write("") }.should_not raise_error
end end
it "returns a length of 0 when writing a blank string" do
@file.write('').should == 0
end
before :each do before :each do
@external = Encoding.default_external @external = Encoding.default_external
@internal = Encoding.default_internal @internal = Encoding.default_internal
@ -40,6 +36,18 @@ describe "IO#write on a file" do
Encoding.default_internal = @internal Encoding.default_internal = @internal
end end
it "returns a length of 0 when writing a blank string" do
@file.write('').should == 0
end
it "returns a length of 0 when writing blank strings" do
@file.write('', '', '').should == 0
end
it "returns a length of 0 when passed no arguments" do
@file.write().should == 0
end
it "returns the number of bytes written" do it "returns the number of bytes written" do
@file.write("hellø").should == 6 @file.write("hellø").should == 6
end end
@ -54,6 +62,18 @@ describe "IO#write on a file" do
File.binread(@filename).bytes.should == [159] File.binread(@filename).bytes.should == [159]
end end
it "does not modify arguments when passed multiple arguments and external encoding not set" do
a, b = "a".freeze, "b".freeze
File.open(@filename, "w") do |f|
f.write(a, b)
end
File.binread(@filename).bytes.should == [97, 98]
a.encoding.should == Encoding::UTF_8
b.encoding.should == Encoding::UTF_8
end
it "uses the encoding from the given option for non-ascii encoding" do it "uses the encoding from the given option for non-ascii encoding" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file| File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("hi").should == 8 file.write("hi").should == 8
@ -61,8 +81,25 @@ describe "IO#write on a file" do
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000" File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end end
it "uses an :open_args option" do it "uses the encoding from the given option for non-ascii encoding even if in binary mode" do
IO.write(@filename, 'hi', open_args: ["w", nil, {encoding: Encoding::UTF_32LE}]).should == 8 File.open(@filename, "w", encoding: Encoding::UTF_32LE, binmode: true) do |file|
file.should.binmode?
file.write("hi").should == 8
end
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
File.open(@filename, "wb", encoding: Encoding::UTF_32LE) do |file|
file.should.binmode?
file.write("hi").should == 8
end
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end
it "uses the encoding from the given option for non-ascii encoding when multiple arguments passes" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("h", "i").should == 8
end
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end end
it "raises a invalid byte sequence error if invalid bytes are being written" do it "raises a invalid byte sequence error if invalid bytes are being written" do
@ -82,6 +119,20 @@ describe "IO#write on a file" do
res = "H#{ë}ll#{ö}" res = "H#{ë}ll#{ö}"
File.binread(@filename).should == res.force_encoding(Encoding::BINARY) File.binread(@filename).should == res.force_encoding(Encoding::BINARY)
end end
platform_is_not :windows do
it "writes binary data if no encoding is given and multiple arguments passed" do
File.open(@filename, "w") do |file|
file.write("\x87".b, "ą") # 0x87 isn't a valid UTF-8 binary representation of a character
end
File.binread(@filename).bytes.should == [0x87, 0xC4, 0x85]
File.open(@filename, "w") do |file|
file.write("\x61".encode("utf-32le"), "ą")
end
File.binread(@filename).bytes.should == [0x61, 0x00, 0x00, 0x00, 0xC4, 0x85]
end
end
end end
describe "IO.write" do describe "IO.write" do
@ -96,10 +147,38 @@ describe "IO.write" do
File.read(@filename).should == "\0\0hi" File.read(@filename).should == "\0\0hi"
end end
it "requires mode to be specified in :open_args" do
-> {
IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true}])
}.should raise_error(IOError, "not opened for writing")
IO.write(@filename, 'hi', open_args: ["w", {encoding: Encoding::UTF_32LE, binmode: true}]).should == 8
IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, mode: "w"}]).should == 8
end
it "requires mode to be specified in :open_args even if flags option passed" do
-> {
IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT}])
}.should raise_error(IOError, "not opened for writing")
IO.write(@filename, 'hi', open_args: ["w", {encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT}]).should == 8
IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT, mode: "w"}]).should == 8
end
it "uses the given encoding and returns the number of bytes written" do it "uses the given encoding and returns the number of bytes written" do
IO.write(@filename, 'hi', mode: "w", encoding: Encoding::UTF_32LE).should == 8 IO.write(@filename, 'hi', mode: "w", encoding: Encoding::UTF_32LE).should == 8
end end
it "raises ArgumentError if encoding is specified in mode parameter and is given as :encoding option" do
-> {
IO.write(@filename, 'hi', mode: "w:UTF-16LE:UTF-16BE", encoding: Encoding::UTF_32LE)
}.should raise_error(ArgumentError, "encoding specified twice")
-> {
IO.write(@filename, 'hi', mode: "w:UTF-16BE", encoding: Encoding::UTF_32LE)
}.should raise_error(ArgumentError, "encoding specified twice")
end
it "writes the file with the permissions in the :perm parameter" do it "writes the file with the permissions in the :perm parameter" do
rm_r @filename rm_r @filename
IO.write(@filename, 'write :perm spec', mode: "w", perm: 0o755).should == 16 IO.write(@filename, 'write :perm spec', mode: "w", perm: 0o755).should == 16

View File

@ -573,10 +573,17 @@ describe "Integer() given a String and base", shared: true do
-> { Integer("0#{d}1", base) }.should raise_error(ArgumentError) -> { Integer("0#{d}1", base) }.should raise_error(ArgumentError)
end end
end end
end
it "raises an ArgumentError if a base is given for a non-String value" do it "raises an ArgumentError if a base is given for a non-String value" do
-> { Integer(98, 15) }.should raise_error(ArgumentError) -> { Integer(98, 15) }.should raise_error(ArgumentError)
end end
it "tries to convert the base to an integer using to_int" do
obj = mock('8')
obj.should_receive(:to_int).and_return(8)
Integer("777", obj).should == 0777
end end
describe "when passed exception: false" do describe "when passed exception: false" do

View File

@ -1,67 +1,16 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative '../../shared/kernel/at_exit'
describe "Kernel.at_exit" do describe "Kernel.at_exit" do
it_behaves_like :kernel_at_exit, :at_exit
it "is a private method" do it "is a private method" do
Kernel.should have_private_instance_method(:at_exit) Kernel.should have_private_instance_method(:at_exit)
end end
it "runs after all other code" do it "raises ArgumentError if called without a block" do
ruby_exe("at_exit {print 5}; print 6").should == "65" -> { at_exit }.should raise_error(ArgumentError, "called without a block")
end
it "runs in reverse order of registration" do
code = "at_exit {print 4};at_exit {print 5}; print 6; at_exit {print 7}"
ruby_exe(code).should == "6754"
end
it "allows calling exit inside at_exit handler" do
code = "at_exit {print 3}; at_exit {print 4; exit; print 5}; at_exit {print 6}"
ruby_exe(code).should == "643"
end
it "gives access to the last raised exception" do
code = <<-EOC
at_exit do
puts "The exception matches: \#{$! == $exception} (message=\#{$!.message})"
end
begin
raise "foo"
rescue => $exception
raise
end
EOC
result = ruby_exe(code, args: "2>&1", exit_status: 1)
result.lines.should.include?("The exception matches: true (message=foo)\n")
end
it "both exceptions in at_exit and in the main script are printed" do
code = 'at_exit { raise "at_exit_error" }; raise "main_script_error"'
result = ruby_exe(code, args: "2>&1", exit_status: 1)
result.should.include?('at_exit_error (RuntimeError)')
result.should.include?('main_script_error (RuntimeError)')
end
it "decides the exit status if both at_exit and the main script raise SystemExit" do
ruby_exe('at_exit { exit 43 }; exit 42', args: "2>&1", exit_status: 43)
$?.exitstatus.should == 43
end
it "runs all at_exit even if some raise exceptions" do
code = 'at_exit { STDERR.puts "last" }; at_exit { exit 43 }; at_exit { STDERR.puts "first" }; exit 42'
result = ruby_exe(code, args: "2>&1", exit_status: 43)
result.should == "first\nlast\n"
$?.exitstatus.should == 43
end
it "runs at_exit handlers even if the main script fails to parse" do
script = fixture(__FILE__, "at_exit.rb")
result = ruby_exe('{', options: "-r#{script}", args: "2>&1", exit_status: 1)
$?.should_not.success?
result.should.include?("at_exit ran\n")
result.should.include?("syntax error")
end end
end end

View File

@ -10,6 +10,10 @@ describe "Kernel#exit" do
it_behaves_like :process_exit, :exit, KernelSpecs::Method.new it_behaves_like :process_exit, :exit, KernelSpecs::Method.new
end end
describe "Kernel.exit" do
it_behaves_like :process_exit, :exit, Kernel
end
describe "Kernel#exit!" do describe "Kernel#exit!" do
it "is a private method" do it "is a private method" do
Kernel.should have_private_instance_method(:exit!) Kernel.should have_private_instance_method(:exit!)
@ -18,10 +22,6 @@ describe "Kernel#exit!" do
it_behaves_like :process_exit!, :exit!, "self" it_behaves_like :process_exit!, :exit!, "self"
end end
describe "Kernel.exit" do
it_behaves_like :process_exit, :exit, Kernel
end
describe "Kernel.exit!" do describe "Kernel.exit!" do
it_behaves_like :process_exit!, :exit!, Kernel it_behaves_like :process_exit!, :exit!, "Kernel"
end end

View File

@ -1,3 +0,0 @@
at_exit do
STDERR.puts "at_exit ran"
end

View File

@ -72,6 +72,17 @@ describe "Kernel#open" do
-> { open }.should raise_error(ArgumentError) -> { open }.should raise_error(ArgumentError)
end end
ruby_version_is "3.0" do
it "accepts options as keyword arguments" do
@file = open(@name, "r", 0666, flags: File::CREAT)
@file.should be_kind_of(File)
-> {
open(@name, "r", 0666, {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
end
describe "when given an object that responds to to_open" do describe "when given an object that responds to to_open" do
before :each do before :each do
ScratchPad.clear ScratchPad.clear
@ -109,11 +120,21 @@ describe "Kernel#open" do
it "passes its arguments onto #to_open" do it "passes its arguments onto #to_open" do
obj = mock('to_open') obj = mock('to_open')
obj.should_receive(:to_open).with(1,2,3) obj.should_receive(:to_open).with(1, 2, 3)
open(obj, 1, 2, 3) open(obj, 1, 2, 3)
end end
it "passes keyword arguments onto #to_open as keyword arguments if to_open accepts them" do
obj = Object.new
def obj.to_open(*args, **kw)
ScratchPad << {args: args, kw: kw}
end
ScratchPad.record []
open(obj, 1, 2, 3, a: "b")
ScratchPad.recorded.should == [args: [1, 2, 3], kw: {a: "b"}]
end
it "passes the return value from #to_open to a block" do it "passes the return value from #to_open to a block" do
obj = mock('to_open') obj = mock('to_open')
obj.should_receive(:to_open).and_return(:value) obj.should_receive(:to_open).and_return(:value)

View File

@ -20,10 +20,26 @@ describe "Kernel#singleton_class" do
end end
it "raises TypeError for Integer" do it "raises TypeError for Integer" do
-> { 123.singleton_class }.should raise_error(TypeError) -> { 123.singleton_class }.should raise_error(TypeError, "can't define singleton")
end
it "raises TypeError for Float" do
-> { 3.14.singleton_class }.should raise_error(TypeError, "can't define singleton")
end end
it "raises TypeError for Symbol" do it "raises TypeError for Symbol" do
-> { :foo.singleton_class }.should raise_error(TypeError) -> { :foo.singleton_class }.should raise_error(TypeError, "can't define singleton")
end
it "raises TypeError for a frozen deduplicated String" do
-> { (-"string").singleton_class }.should raise_error(TypeError, "can't define singleton")
-> { a = -"string"; a.singleton_class }.should raise_error(TypeError, "can't define singleton")
-> { a = "string"; (-a).singleton_class }.should raise_error(TypeError, "can't define singleton")
end
it "returns a frozen singleton class if object is frozen" do
obj = Object.new
obj.freeze
obj.singleton_class.frozen?.should be_true
end end
end end

View File

@ -8,7 +8,11 @@ describe :marshal_load, shared: true do
it "raises an ArgumentError when the dumped data is truncated" do it "raises an ArgumentError when the dumped data is truncated" do
obj = {first: 1, second: 2, third: 3} obj = {first: 1, second: 2, third: 3}
-> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError) -> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError, "marshal data too short")
end
it "raises an ArgumentError when the argument is empty String" do
-> { Marshal.send(@method, "") }.should raise_error(ArgumentError, "marshal data too short")
end end
it "raises an ArgumentError when the dumped class is missing" do it "raises an ArgumentError when the dumped class is missing" do

View File

@ -67,6 +67,11 @@ describe "Module#const_source_location" do
end end
describe "with statically assigned constants" do describe "with statically assigned constants" do
it "works for the module and class keywords" do
ConstantSpecs.const_source_location(:ModuleB).should == [@constants_fixture_path, ConstantSpecs::ModuleB::LINE]
ConstantSpecs.const_source_location(:ClassA).should == [@constants_fixture_path, ConstantSpecs::ClassA::LINE]
end
it "searches location path the immediate class or module first" do it "searches location path the immediate class or module first" do
ConstantSpecs::ClassA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CONST10_LINE] ConstantSpecs::ClassA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CONST10_LINE]
ConstantSpecs::ModuleA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ModuleA::CS_CONST10_LINE] ConstantSpecs::ModuleA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ModuleA::CS_CONST10_LINE]
@ -133,7 +138,7 @@ describe "Module#const_source_location" do
it "calls #to_str to convert the given name to a String" do it "calls #to_str to convert the given name to a String" do
name = mock("ClassA") name = mock("ClassA")
name.should_receive(:to_str).and_return("ClassA") name.should_receive(:to_str).and_return("ClassA")
ConstantSpecs.const_source_location(name).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CLASS_A_LINE] ConstantSpecs.const_source_location(name).should == [@constants_fixture_path, ConstantSpecs::ClassA::LINE]
end end
it "raises a TypeError if conversion to a String by calling #to_str fails" do it "raises a TypeError if conversion to a String by calling #to_str fails" do

View File

@ -2,6 +2,10 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
describe "Module#method_added" do describe "Module#method_added" do
before :each do
ScratchPad.record []
end
it "is a private instance method" do it "is a private instance method" do
Module.should have_private_instance_method(:method_added) Module.should have_private_instance_method(:method_added)
end end
@ -13,8 +17,6 @@ describe "Module#method_added" do
end end
it "is called when a new instance method is defined in self" do it "is called when a new instance method is defined in self" do
ScratchPad.record []
Module.new do Module.new do
def self.method_added(name) def self.method_added(name)
ScratchPad << name ScratchPad << name
@ -32,8 +34,6 @@ describe "Module#method_added" do
it "is not called when a singleton method is added" do it "is not called when a singleton method is added" do
# obj.singleton_method_added is called instead # obj.singleton_method_added is called instead
ScratchPad.record []
klass = Class.new klass = Class.new
def klass.method_added(name) def klass.method_added(name)
ScratchPad << name ScratchPad << name
@ -60,8 +60,71 @@ describe "Module#method_added" do
m.should_not have_method(:method_to_undef) m.should_not have_method(:method_to_undef)
end end
it "is not called when a method changes visibility" do
Module.new do
def public_method
end
def private_method
end
def self.method_added(name)
ScratchPad << name
end
public :public_method
private :public_method
private :private_method
public :private_method
end
ScratchPad.recorded.should == []
end
it "is called when using #private in a subclass" do
parent = Class.new do
def foo
end
end
Class.new(parent) do
def self.method_added(name)
ScratchPad << name
end
# Create an instance as that might initialize some method lookup caches, which is interesting to test
self.new.foo
private :foo
public :foo
end
ScratchPad.recorded.should == [:foo]
end
it "is not called when a method is copied via module_function, rather #singleton_method_added is called" do
Module.new do
def mod_function
end
def self.method_added(name)
ScratchPad << [:method_added, name]
end
def self.singleton_method_added(name)
ScratchPad << [:singleton_method_added, name]
end
ScratchPad.record []
module_function :mod_function
end
ScratchPad.recorded.should == [[:singleton_method_added, :mod_function]]
end
it "is called with a precise caller location with the line of the 'def'" do it "is called with a precise caller location with the line of the 'def'" do
ScratchPad.record []
line = nil line = nil
Module.new do Module.new do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative 'shared/abs' require_relative 'shared/abs'
describe "Numeric#magnitude" do describe "Numeric#magnitude" do

View File

@ -6,5 +6,5 @@ describe "Process.exit" do
end end
describe "Process.exit!" do describe "Process.exit!" do
it_behaves_like :process_exit!, :exit!, Process it_behaves_like :process_exit!, :exit!, "Process"
end end

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
describe "Random.new" do describe "Random.new" do
it "returns a new instance of Random" do it "returns a new instance of Random" do
Random.new.should be_an_instance_of(Random) Random.new.should be_an_instance_of(Random)

View File

@ -474,10 +474,14 @@ describe "Range#step" do
end end
end end
# We use .take below to ensure the enumerator works
# because that's an Enumerable method and so it uses the Enumerator behavior
# not just a method overridden in Enumerator::ArithmeticSequence.
describe "type" do describe "type" do
context "when both begin and end are numerics" do context "when both begin and end are numerics" do
it "returns an instance of Enumerator::ArithmeticSequence" do it "returns an instance of Enumerator::ArithmeticSequence" do
(1..10).step.class.should == Enumerator::ArithmeticSequence (1..10).step.class.should == Enumerator::ArithmeticSequence
(1..10).step(3).take(4).should == [1, 4, 7, 10]
end end
end end
@ -490,10 +494,12 @@ describe "Range#step" do
context "when range is endless" do context "when range is endless" do
it "returns an instance of Enumerator::ArithmeticSequence when begin is numeric" do it "returns an instance of Enumerator::ArithmeticSequence when begin is numeric" do
(1..).step.class.should == Enumerator::ArithmeticSequence (1..).step.class.should == Enumerator::ArithmeticSequence
(1..).step(2).take(3).should == [1, 3, 5]
end end
it "returns an instance of Enumerator when begin is not numeric" do it "returns an instance of Enumerator when begin is not numeric" do
("a"..).step.class.should == Enumerator ("a"..).step.class.should == Enumerator
("a"..).step(2).take(3).should == %w[a c e]
end end
end end
@ -506,6 +512,7 @@ describe "Range#step" do
context "when begin and end are not numerics" do context "when begin and end are not numerics" do
it "returns an instance of Enumerator" do it "returns an instance of Enumerator" do
("a".."z").step.class.should == Enumerator ("a".."z").step.class.should == Enumerator
("a".."z").step(3).take(4).should == %w[a d g j]
end end
end end
end end

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/abs' require_relative '../../shared/rational/abs'
describe "Rational#abs" do describe "Rational#abs" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/ceil' require_relative '../../shared/rational/ceil'
describe "Rational#ceil" do describe "Rational#ceil" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/coerce' require_relative '../../shared/rational/coerce'
describe "Rational#coerce" do describe "Rational#coerce" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/comparison' require_relative '../../shared/rational/comparison'
describe "Rational#<=> when passed a Rational object" do describe "Rational#<=> when passed a Rational object" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/denominator' require_relative '../../shared/rational/denominator'
describe "Rational#denominator" do describe "Rational#denominator" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/div' require_relative '../../shared/rational/div'
describe "Rational#div" do describe "Rational#div" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/divide' require_relative '../../shared/rational/divide'
require_relative '../../shared/rational/arithmetic_exception_in_coerce' require_relative '../../shared/rational/arithmetic_exception_in_coerce'

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/divmod' require_relative '../../shared/rational/divmod'
describe "Rational#divmod when passed a Rational" do describe "Rational#divmod when passed a Rational" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/equal_value' require_relative '../../shared/rational/equal_value'
describe "Rational#==" do describe "Rational#==" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/exponent' require_relative '../../shared/rational/exponent'
describe "Rational#**" do describe "Rational#**" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/fdiv' require_relative '../../shared/rational/fdiv'
describe "Rational#fdiv" do describe "Rational#fdiv" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/floor' require_relative '../../shared/rational/floor'
describe "Rational#floor" do describe "Rational#floor" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/hash' require_relative '../../shared/rational/hash'
describe "Rational#hash" do describe "Rational#hash" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/inspect' require_relative '../../shared/rational/inspect'
describe "Rational#inspect" do describe "Rational#inspect" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
describe "Rational#integer?" do describe "Rational#integer?" do
# Guard against the Mathn library # Guard against the Mathn library
guard -> { !defined?(Math.rsqrt) } do guard -> { !defined?(Math.rsqrt) } do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/abs' require_relative '../../shared/rational/abs'
describe "Rational#abs" do describe "Rational#abs" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/modulo' require_relative '../../shared/rational/modulo'
describe "Rational#%" do describe "Rational#%" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/multiply' require_relative '../../shared/rational/multiply'
require_relative '../../shared/rational/arithmetic_exception_in_coerce' require_relative '../../shared/rational/arithmetic_exception_in_coerce'

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/numerator' require_relative '../../shared/rational/numerator'
describe "Rational#numerator" do describe "Rational#numerator" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/plus' require_relative '../../shared/rational/plus'
require_relative '../../shared/rational/arithmetic_exception_in_coerce' require_relative '../../shared/rational/arithmetic_exception_in_coerce'

Some files were not shown because too many files have changed in this diff Show More