diff options
Diffstat (limited to 'spec/ruby/core/array')
73 files changed, 1391 insertions, 1127 deletions
diff --git a/spec/ruby/core/array/all_spec.rb b/spec/ruby/core/array/all_spec.rb new file mode 100644 index 0000000000..680e8c26fa --- /dev/null +++ b/spec/ruby/core/array/all_spec.rb @@ -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 diff --git a/spec/ruby/core/array/any_spec.rb b/spec/ruby/core/array/any_spec.rb index 09d949fe6e..b51ce62f0f 100644 --- a/spec/ruby/core/array/any_spec.rb +++ b/spec/ruby/core/array/any_spec.rb @@ -1,4 +1,5 @@ require_relative '../../spec_helper' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#any?" do describe 'with no block given (a default block of { |x| x } is implicit)' do @@ -19,6 +20,9 @@ describe "Array#any?" do end 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 empty_array = [] 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 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 diff --git a/spec/ruby/core/array/assoc_spec.rb b/spec/ruby/core/array/assoc_spec.rb index f8479d763c..f0be3de795 100644 --- a/spec/ruby/core/array/assoc_spec.rb +++ b/spec/ruby/core/array/assoc_spec.rb @@ -6,7 +6,7 @@ describe "Array#assoc" do s1 = ["colors", "red", "blue", "green"] s2 = [:letters, "a", "b", "c"] s3 = [4] - s4 = ["colors", "cyan", "yellow", "magenda"] + s4 = ["colors", "cyan", "yellow", "magenta"] s5 = [:letters, "a", "i", "u"] s_nil = [nil, nil] a = [s1, s2, s3, s4, s5, s_nil] @@ -37,4 +37,16 @@ describe "Array#assoc" do a.assoc(s1.first).should equal(s1) a.assoc(s2.first).should equal(s2) end + + it "calls to_ary on non-array elements" do + s1 = [1, 2] + s2 = ArraySpecs::ArrayConvertible.new(2, 3) + a = [s1, s2] + + s1.should_not_receive(:to_ary) + a.assoc(s1.first).should equal(s1) + + a.assoc(2).should == [2, 3] + s2.called.should equal(:to_ary) + end end diff --git a/spec/ruby/core/array/bsearch_index_spec.rb b/spec/ruby/core/array/bsearch_index_spec.rb index df2c7c098e..94d85b37f3 100644 --- a/spec/ruby/core/array/bsearch_index_spec.rb +++ b/spec/ruby/core/array/bsearch_index_spec.rb @@ -63,10 +63,6 @@ describe "Array#bsearch_index" do @array.bsearch_index { |x| -1 }.should be_nil end - it "returns the middle element when block always returns zero" do - @array.bsearch_index { |x| 0 }.should == 2 - end - context "magnitude does not effect the result" do it "returns the index of any matched elements where element is between 4n <= xn < 8n" do [1, 2].should include(@array.bsearch_index { |x| (1 - x / 4) * (2**100) }) diff --git a/spec/ruby/core/array/clear_spec.rb b/spec/ruby/core/array/clear_spec.rb index bddc672d3b..81ba56e01e 100644 --- a/spec/ruby/core/array/clear_spec.rb +++ b/spec/ruby/core/array/clear_spec.rb @@ -20,30 +20,10 @@ describe "Array#clear" do a.size.should == 0 end - ruby_version_is ''...'2.7' do - it "keeps tainted status" do - a = [1] - a.taint - a.tainted?.should be_true - a.clear - a.tainted?.should be_true - end - end - it "does not accept any arguments" do -> { [1].clear(true) }.should raise_error(ArgumentError) end - ruby_version_is ''...'2.7' do - it "keeps untrusted status" do - a = [1] - a.untrust - a.untrusted?.should be_true - a.clear - a.untrusted?.should be_true - end - end - it "raises a FrozenError on a frozen array" do a = [1] a.freeze diff --git a/spec/ruby/core/array/compact_spec.rb b/spec/ruby/core/array/compact_spec.rb index aa3c1c0446..83b3fa2a89 100644 --- a/spec/ruby/core/array/compact_spec.rb +++ b/spec/ruby/core/array/compact_spec.rb @@ -21,20 +21,6 @@ describe "Array#compact" do it "does not return subclass instance for Array subclasses" do ArraySpecs::MyArray[1, 2, 3, nil].compact.should be_an_instance_of(Array) end - - ruby_version_is ''...'2.7' do - it "does not keep tainted status even if all elements are removed" do - a = [nil, nil] - a.taint - a.compact.tainted?.should be_false - end - - it "does not keep untrusted status even if all elements are removed" do - a = [nil, nil] - a.untrust - a.compact.untrusted?.should be_false - end - end end describe "Array#compact!" do @@ -59,22 +45,6 @@ describe "Array#compact!" do [1, 2, false, 3].compact!.should == nil end - ruby_version_is ''...'2.7' do - it "keeps tainted status even if all elements are removed" do - a = [nil, nil] - a.taint - a.compact! - a.tainted?.should be_true - end - - it "keeps untrusted status even if all elements are removed" do - a = [nil, nil] - a.untrust - a.compact! - a.untrusted?.should be_true - end - end - it "raises a FrozenError on a frozen array" do -> { ArraySpecs.frozen_array.compact! }.should raise_error(FrozenError) end diff --git a/spec/ruby/core/array/concat_spec.rb b/spec/ruby/core/array/concat_spec.rb index da6763bfd6..f3cab9c17c 100644 --- a/spec/ruby/core/array/concat_spec.rb +++ b/spec/ruby/core/array/concat_spec.rb @@ -41,64 +41,6 @@ describe "Array#concat" do -> { ArraySpecs.frozen_array.concat([]) }.should raise_error(FrozenError) end - ruby_version_is ''...'2.7' do - it "keeps tainted status" do - ary = [1, 2] - ary.taint - ary.concat([3]) - ary.tainted?.should be_true - ary.concat([]) - ary.tainted?.should be_true - end - - it "is not infected by the other" do - ary = [1,2] - other = [3]; other.taint - ary.tainted?.should be_false - ary.concat(other) - ary.tainted?.should be_false - end - - it "keeps the tainted status of elements" do - ary = [ Object.new, Object.new, Object.new ] - ary.each {|x| x.taint } - - ary.concat([ Object.new ]) - ary[0].tainted?.should be_true - ary[1].tainted?.should be_true - ary[2].tainted?.should be_true - ary[3].tainted?.should be_false - end - - it "keeps untrusted status" do - ary = [1, 2] - ary.untrust - ary.concat([3]) - ary.untrusted?.should be_true - ary.concat([]) - ary.untrusted?.should be_true - end - - it "is not infected untrustedness by the other" do - ary = [1,2] - other = [3]; other.untrust - ary.untrusted?.should be_false - ary.concat(other) - ary.untrusted?.should be_false - end - - it "keeps the untrusted status of elements" do - ary = [ Object.new, Object.new, Object.new ] - ary.each {|x| x.untrust } - - ary.concat([ Object.new ]) - ary[0].untrusted?.should be_true - ary[1].untrusted?.should be_true - ary[2].untrusted?.should be_true - ary[3].untrusted?.should be_false - end - end - it "appends elements to an Array with enough capacity that has been shifted" do ary = [1, 2, 3, 4, 5] 2.times { ary.shift } diff --git a/spec/ruby/core/array/count_spec.rb b/spec/ruby/core/array/count_spec.rb index eaf275aeb7..e778233c16 100644 --- a/spec/ruby/core/array/count_spec.rb +++ b/spec/ruby/core/array/count_spec.rb @@ -1,4 +1,5 @@ require_relative '../../spec_helper' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#count" 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 [:a, :b, :c].count { |s| s != :b }.should == 2 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 diff --git a/spec/ruby/core/array/deconstruct_spec.rb b/spec/ruby/core/array/deconstruct_spec.rb index 2b07152dfc..ad67abe47b 100644 --- a/spec/ruby/core/array/deconstruct_spec.rb +++ b/spec/ruby/core/array/deconstruct_spec.rb @@ -1,11 +1,9 @@ require_relative '../../spec_helper' -ruby_version_is "2.7" do - describe "Array#deconstruct" do - it "returns self" do - array = [1] +describe "Array#deconstruct" do + it "returns self" do + array = [1] - array.deconstruct.should equal array - end + array.deconstruct.should equal array end end diff --git a/spec/ruby/core/array/delete_at_spec.rb b/spec/ruby/core/array/delete_at_spec.rb index fc225a03f1..80ec643702 100644 --- a/spec/ruby/core/array/delete_at_spec.rb +++ b/spec/ruby/core/array/delete_at_spec.rb @@ -38,26 +38,4 @@ describe "Array#delete_at" do it "raises a FrozenError on a frozen array" do -> { [1,2,3].freeze.delete_at(0) }.should raise_error(FrozenError) end - - ruby_version_is ''...'2.7' do - it "keeps tainted status" do - ary = [1, 2] - ary.taint - ary.tainted?.should be_true - ary.delete_at(0) - ary.tainted?.should be_true - ary.delete_at(0) # now empty - ary.tainted?.should be_true - end - - it "keeps untrusted status" do - ary = [1, 2] - ary.untrust - ary.untrusted?.should be_true - ary.delete_at(0) - ary.untrusted?.should be_true - ary.delete_at(0) # now empty - ary.untrusted?.should be_true - end - end end diff --git a/spec/ruby/core/array/delete_if_spec.rb b/spec/ruby/core/array/delete_if_spec.rb index e1931220f5..10972eee0e 100644 --- a/spec/ruby/core/array/delete_if_spec.rb +++ b/spec/ruby/core/array/delete_if_spec.rb @@ -2,6 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/enumeratorize' require_relative 'shared/delete_if' +require_relative 'shared/iterable_and_tolerating_size_increasing' require_relative '../enumerable/shared/enumeratorized' describe "Array#delete_if" do @@ -47,22 +48,35 @@ describe "Array#delete_if" do -> { ArraySpecs.empty_frozen_array.delete_if {} }.should raise_error(FrozenError) end - ruby_version_is ''...'2.7' do - it "keeps tainted status" do - @a.taint - @a.tainted?.should be_true - @a.delete_if{ true } - @a.tainted?.should be_true + 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 - it "keeps untrusted status" do - @a.untrust - @a.untrusted?.should be_true - @a.delete_if{ true } - @a.untrusted?.should be_true + 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 :delete_if, :delete_if + + @value_to_return = -> _ { false } + it_behaves_like :array_iterable_and_tolerating_size_increasing, :delete_if end diff --git a/spec/ruby/core/array/delete_spec.rb b/spec/ruby/core/array/delete_spec.rb index 5d53c74e47..dddbbe6bd3 100644 --- a/spec/ruby/core/array/delete_spec.rb +++ b/spec/ruby/core/array/delete_spec.rb @@ -43,26 +43,4 @@ describe "Array#delete" do it "raises a FrozenError on a frozen array" do -> { [1, 2, 3].freeze.delete(1) }.should raise_error(FrozenError) end - - ruby_version_is ''...'2.7' do - it "keeps tainted status" do - a = [1, 2] - a.taint - a.tainted?.should be_true - a.delete(2) - a.tainted?.should be_true - a.delete(1) # now empty - a.tainted?.should be_true - end - - it "keeps untrusted status" do - a = [1, 2] - a.untrust - a.untrusted?.should be_true - a.delete(2) - a.untrusted?.should be_true - a.delete(1) # now empty - a.untrusted?.should be_true - end - end end diff --git a/spec/ruby/core/array/drop_spec.rb b/spec/ruby/core/array/drop_spec.rb index f911fd9018..0ea748e47d 100644 --- a/spec/ruby/core/array/drop_spec.rb +++ b/spec/ruby/core/array/drop_spec.rb @@ -50,15 +50,7 @@ describe "Array#drop" do -> { [1, 2].drop(obj) }.should raise_error(TypeError) end - ruby_version_is ''...'3.0' do - it 'returns a subclass instance for Array subclasses' do - ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should be_an_instance_of(ArraySpecs::MyArray) - end - end - - ruby_version_is '3.0' do - it 'returns a Array instance for Array subclasses' do - ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should be_an_instance_of(Array) - end + it 'returns a Array instance for Array subclasses' do + ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should be_an_instance_of(Array) end end diff --git a/spec/ruby/core/array/drop_while_spec.rb b/spec/ruby/core/array/drop_while_spec.rb index bb783d22a5..bd46e8b882 100644 --- a/spec/ruby/core/array/drop_while_spec.rb +++ b/spec/ruby/core/array/drop_while_spec.rb @@ -1,7 +1,11 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/iterable_and_tolerating_size_increasing' 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 [1, 2, 3, 4].drop_while { |n| n < 4 }.should == [4] end @@ -14,15 +18,7 @@ describe "Array#drop_while" do [1, 2, 3, false, 5].drop_while { |n| n }.should == [false, 5] end - ruby_version_is ''...'3.0' do - it 'returns a subclass instance for Array subclasses' do - ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should be_an_instance_of(ArraySpecs::MyArray) - end - end - - ruby_version_is '3.0' do - it 'returns a Array instance for Array subclasses' do - ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should be_an_instance_of(Array) - end + it 'returns a Array instance for Array subclasses' do + ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should be_an_instance_of(Array) end end diff --git a/spec/ruby/core/array/each_index_spec.rb b/spec/ruby/core/array/each_index_spec.rb index 51af5842c4..3a4bca9251 100644 --- a/spec/ruby/core/array/each_index_spec.rb +++ b/spec/ruby/core/array/each_index_spec.rb @@ -5,7 +5,7 @@ require_relative '../enumerable/shared/enumeratorized' # Modifying a collection while the contents are being iterated # gives undefined behavior. See -# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633 +# https://blade.ruby-lang.org/ruby-core/23633 describe "Array#each_index" do before :each do @@ -40,3 +40,19 @@ describe "Array#each_index" do it_behaves_like :enumeratorize, :each_index it_behaves_like :enumeratorized_with_origin_size, :each_index, [1,2,3] 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 diff --git a/spec/ruby/core/array/each_spec.rb b/spec/ruby/core/array/each_spec.rb index 256647d61e..57d6082f01 100644 --- a/spec/ruby/core/array/each_spec.rb +++ b/spec/ruby/core/array/each_spec.rb @@ -1,11 +1,13 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/enumeratorize' +require_relative 'shared/iterable_and_tolerating_size_increasing' require_relative '../enumerable/shared/enumeratorized' -# Modifying a collection while the contents are being iterated -# gives undefined behavior. See -# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633 +# Mutating the array while it is being iterated is discouraged as it can result in confusing behavior. +# Yet a Ruby implementation must not crash in such a case, and following the simple CRuby behavior makes sense. +# CRuby simply reads the array storage and checks the size for every iteration; +# like `i = 0; while i < size; yield self[i]; end` describe "Array#each" do it "yields each element to the block" do @@ -15,6 +17,34 @@ describe "Array#each" do a.should == [1, 2, 3] end + it "yields each element to the block even if the array is changed during iteration" do + a = [1, 2, 3, 4, 5] + iterated = [] + a.each { |x| iterated << x; a << x+5 if x.even? } + iterated.should == [1, 2, 3, 4, 5, 7, 9] + end + + it "yields only elements that are still in the array" do + a = [0, 1, 2, 3, 4] + iterated = [] + a.each { |x| iterated << x; a.pop if x.even? } + iterated.should == [0, 1, 2] + end + + it "yields elements based on an internal index" do + a = [0, 1, 2, 3, 4] + iterated = [] + a.each { |x| iterated << x; a.shift if x.even? } + iterated.should == [0, 2, 4] + end + + it "yields the same element multiple times if inserting while iterating" do + a = [1, 2] + iterated = [] + a.each { |x| iterated << x; a.unshift(0) if a.size == 2 } + iterated.should == [1, 1, 2] + end + it "yields each element to a block that takes multiple arguments" do a = [[1, 2], :a, [3, 4]] b = [] @@ -46,3 +76,7 @@ describe "Array#each" do it_behaves_like :enumeratorize, :each it_behaves_like :enumeratorized_with_origin_size, :each, [1,2,3] end + +describe "Array#each" do + it_behaves_like :array_iterable_and_tolerating_size_increasing, :each +end diff --git a/spec/ruby/core/array/element_set_spec.rb b/spec/ruby/core/array/element_set_spec.rb index 1e192c100f..df5ca9582e 100644 --- a/spec/ruby/core/array/element_set_spec.rb +++ b/spec/ruby/core/array/element_set_spec.rb @@ -481,50 +481,48 @@ describe "Array#[]= with [m..]" do end end -ruby_version_is "2.7" do - describe "Array#[]= with [..n] and [...n]" 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 == [nil, 4, 5] - a[eval("(...2)")] = nil - a.should == [nil, 5] - end +describe "Array#[]= with [..n] and [...n]" 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[(..2)] = nil + a.should == [nil, 4, 5] + a[(...2)] = nil + a.should == [nil, 5] + end - it "just sets the section defined by range to nil if n < 0 and the rhs is nil" do - a = [1, 2, 3, 4, 5] - a[eval("(..-3)")] = nil - a.should == [nil, 4, 5] - a[eval("(...-1)")] = [nil, 5] - end + it "just sets the section defined by range to nil if n < 0 and the rhs is nil" do + a = [1, 2, 3, 4, 5] + a[(..-3)] = nil + a.should == [nil, 4, 5] + a[(...-1)] = [nil, 5] + end - it "replaces the section defined by range" do - a = [6, 5, 4, 3, 2, 1] - a[eval("(...3)")] = 9 - a.should == [9, 3, 2, 1] - a[eval("(..2)")] = [7, 7, 7, 7, 7] - a.should == [7, 7, 7, 7, 7, 1] - end + it "replaces the section defined by range" do + a = [6, 5, 4, 3, 2, 1] + a[(...3)] = 9 + a.should == [9, 3, 2, 1] + a[(..2)] = [7, 7, 7, 7, 7] + a.should == [7, 7, 7, 7, 7, 1] + end - it "replaces the section if n < 0" do - a = [1, 2, 3, 4, 5] - a[eval("(..-2)")] = [7, 8, 9] - a.should == [7, 8, 9, 5] - end + it "replaces the section if n < 0" do + a = [1, 2, 3, 4, 5] + a[(..-2)] = [7, 8, 9] + a.should == [7, 8, 9, 5] + end - it "replaces everything if n > the array size" do - a = [1, 2, 3] - a[eval("(...7)")] = [4] - a.should == [4] - end + it "replaces everything if n > the array size" do + a = [1, 2, 3] + a[(...7)] = [4] + a.should == [4] + end - it "inserts at the beginning if n < negative the array size" do - a = [1, 2, 3] - a[eval("(..-7)")] = [4] - a.should == [4, 1, 2, 3] - a[eval("(...-10)")] = [6] - a.should == [6, 4, 1, 2, 3] - end + it "inserts at the beginning if n < negative the array size" do + a = [1, 2, 3] + a[(..-7)] = [4] + a.should == [4, 1, 2, 3] + a[(...-10)] = [6] + a.should == [6, 4, 1, 2, 3] end end diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb index bfa8db551b..2c3b5d9e84 100644 --- a/spec/ruby/core/array/fill_spec.rb +++ b/spec/ruby/core/array/fill_spec.rb @@ -21,7 +21,7 @@ describe "Array#fill" do it "does not replicate the filler" do ary = [1, 2, 3, 4] - str = "x" + str = +"x" ary.fill(str).should == [str, str, str, str] str << "y" ary.should == [str, str, str, str] @@ -52,11 +52,9 @@ describe "Array#fill" do end it "raises an ArgumentError if 4 or more arguments are passed when no block given" do - -> { [].fill('a') }.should_not raise_error(ArgumentError) - - -> { [].fill('a', 1) }.should_not raise_error(ArgumentError) - - -> { [].fill('a', 1, 2) }.should_not raise_error(ArgumentError) + [].fill('a').should == [] + [].fill('a', 1).should == [] + [].fill('a', 1, 2).should == [nil, 'a', 'a'] -> { [].fill('a', 1, 2, true) }.should raise_error(ArgumentError) end @@ -65,12 +63,52 @@ describe "Array#fill" do end it "raises an ArgumentError if 3 or more arguments are passed when a block given" do - -> { [].fill() {|i|} }.should_not raise_error(ArgumentError) + [].fill() {|i|}.should == [] + [].fill(1) {|i|}.should == [] + [].fill(1, 2) {|i|}.should == [nil, nil, nil] + -> { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError) + end - -> { [].fill(1) {|i|} }.should_not raise_error(ArgumentError) + 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 - -> { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError) - -> { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError) + 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 @@ -169,25 +207,25 @@ describe "Array#fill with (filler, index, length)" do [1, 2, 3, 4, 5].fill(-2, -2, &@never_passed).should == [1, 2, 3, 4, 5] end - # See: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17481 + # See: https://blade.ruby-lang.org/ruby-core/17481 it "does not raise an exception if the given length is negative and its absolute value does not exceed the index" do - -> { [1, 2, 3, 4].fill('a', 3, -1)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill('a', 3, -2)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill('a', 3, -3)}.should_not raise_error(ArgumentError) + [1, 2, 3, 4].fill('a', 3, -1).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill('a', 3, -2).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill('a', 3, -3).should == [1, 2, 3, 4] - -> { [1, 2, 3, 4].fill(3, -1, &@never_passed)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill(3, -2, &@never_passed)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill(3, -3, &@never_passed)}.should_not raise_error(ArgumentError) + [1, 2, 3, 4].fill(3, -1, &@never_passed).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill(3, -2, &@never_passed).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill(3, -3, &@never_passed).should == [1, 2, 3, 4] end it "does not raise an exception even if the given length is negative and its absolute value exceeds the index" do - -> { [1, 2, 3, 4].fill('a', 3, -4)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill('a', 3, -5)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill('a', 3, -10000)}.should_not raise_error(ArgumentError) + [1, 2, 3, 4].fill('a', 3, -4).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill('a', 3, -5).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill('a', 3, -10000).should == [1, 2, 3, 4] - -> { [1, 2, 3, 4].fill(3, -4, &@never_passed)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill(3, -5, &@never_passed)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill(3, -10000, &@never_passed)}.should_not raise_error(ArgumentError) + [1, 2, 3, 4].fill(3, -4, &@never_passed).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill(3, -5, &@never_passed).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill(3, -10000, &@never_passed).should == [1, 2, 3, 4] end it "tries to convert the second and third arguments to Integers using #to_int" do @@ -205,6 +243,12 @@ describe "Array#fill with (filler, index, length)" do -> { [].fill('a', obj) }.should raise_error(TypeError) end + it "raises a TypeError when the length is not numeric" do + -> { [1, 2, 3].fill("x", 1, "foo") }.should raise_error(TypeError, /no implicit conversion of String into Integer/) + -> { [1, 2, 3].fill("x", 1, :"foo") }.should raise_error(TypeError, /no implicit conversion of Symbol into Integer/) + -> { [1, 2, 3].fill("x", 1, Object.new) }.should raise_error(TypeError, /no implicit conversion of Object into Integer/) + end + not_supported_on :opal do it "raises an ArgumentError or RangeError for too-large sizes" do error_types = [RangeError, ArgumentError] @@ -323,10 +367,8 @@ describe "Array#fill with (filler, range)" do [1, 2, 3, 4].fill(eval("(3...)")) { |x| x + 2 }.should == [1, 2, 3, 5] end - ruby_version_is "2.7" do - it "works with beginless ranges" do - [1, 2, 3, 4].fill('x', eval("(..2)")).should == ["x", "x", "x", 4] - [1, 2, 3, 4].fill(eval("(...2)")) { |x| x + 2 }.should == [2, 3, 3, 4] - end + it "works with beginless ranges" do + [1, 2, 3, 4].fill('x', (..2)).should == ["x", "x", "x", 4] + [1, 2, 3, 4].fill((...2)) { |x| x + 2 }.should == [2, 3, 3, 4] end end diff --git a/spec/ruby/core/array/fixtures/classes.rb b/spec/ruby/core/array/fixtures/classes.rb index affb3b49e6..8596245fb8 100644 --- a/spec/ruby/core/array/fixtures/classes.rb +++ b/spec/ruby/core/array/fixtures/classes.rb @@ -40,6 +40,68 @@ module ArraySpecs a end + # Chi squared critical values for tests with n degrees of freedom at 99% confidence. + # Values obtained from NIST Engineering Statistic Handbook at + # https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm + + CHI_SQUARED_CRITICAL_VALUES = [ + 0, + 6.635, 9.210, 11.345, 13.277, 15.086, 16.812, 18.475, 20.090, 21.666, 23.209, + 24.725, 26.217, 27.688, 29.141, 30.578, 32.000, 33.409, 34.805, 36.191, 37.566, + 38.932, 40.289, 41.638, 42.980, 44.314, 45.642, 46.963, 48.278, 49.588, 50.892, + 52.191, 53.486, 54.776, 56.061, 57.342, 58.619, 59.893, 61.162, 62.428, 63.691, + 64.950, 66.206, 67.459, 68.710, 69.957, 71.201, 72.443, 73.683, 74.919, 76.154, + 77.386, 78.616, 79.843, 81.069, 82.292, 83.513, 84.733, 85.950, 87.166, 88.379, + 89.591, 90.802, 92.010, 93.217, 94.422, 95.626, 96.828, 98.028, 99.228, 100.425, + 101.621, 102.816, 104.010, 105.202, 106.393, 107.583, 108.771, 109.958, 111.144, 112.329, + 113.512, 114.695, 115.876, 117.057, 118.236, 119.414, 120.591, 121.767, 122.942, 124.116, + 125.289, 126.462, 127.633, 128.803, 129.973, 131.141, 132.309, 133.476, 134.642, 135.807, + ] + + def self.measure_sample_fairness(size, samples, iters) + ary = Array.new(size) { |x| x } + (samples).times do |i| + chi_results = [] + 3.times do + counts = Array.new(size) { 0 } + expected = iters / size + iters.times do + x = ary.sample(samples)[i] + counts[x] += 1 + end + chi_squared = 0.0 + counts.each do |count| + chi_squared += (((count - expected) ** 2) * 1.0 / expected) + end + chi_results << chi_squared + break if chi_squared <= CHI_SQUARED_CRITICAL_VALUES[size] + end + + chi_results.min.should <= CHI_SQUARED_CRITICAL_VALUES[size] + end + end + + def self.measure_sample_fairness_large_sample_size(size, samples, iters) + ary = Array.new(size) { |x| x } + counts = Array.new(size) { 0 } + expected = iters * samples / size + iters.times do + ary.sample(samples).each do |sample| + counts[sample] += 1 + end + end + chi_squared = 0.0 + counts.each do |count| + chi_squared += (((count - expected) ** 2) * 1.0 / expected) + end + + # Chi squared critical values for tests with 4 degrees of freedom + # Values obtained from NIST Engineering Statistic Handbook at + # https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm + + chi_squared.should <= CHI_SQUARED_CRITICAL_VALUES[size] + end + class MyArray < Array # The #initialize method has a different signature than Array to help # catch places in the specs that do not assert the #initialize is not @@ -98,6 +160,16 @@ module ArraySpecs end end + class ArrayMethodMissing + def initialize(*values, &block) + @values = values; + end + + def method_missing(name, *args) + @values + end + end + class SortSame def <=>(other); 0; end def ==(other); true; end diff --git a/spec/ruby/core/array/fixtures/encoded_strings.rb b/spec/ruby/core/array/fixtures/encoded_strings.rb index 5b85bd0e06..b5888d86ae 100644 --- a/spec/ruby/core/array/fixtures/encoded_strings.rb +++ b/spec/ruby/core/array/fixtures/encoded_strings.rb @@ -2,14 +2,14 @@ module ArraySpecs def self.array_with_usascii_and_7bit_utf8_strings [ - 'foo'.force_encoding('US-ASCII'), + 'foo'.dup.force_encoding('US-ASCII'), 'bar' ] end def self.array_with_usascii_and_utf8_strings [ - 'foo'.force_encoding('US-ASCII'), + 'foo'.dup.force_encoding('US-ASCII'), 'báz' ] end @@ -17,7 +17,7 @@ module ArraySpecs def self.array_with_7bit_utf8_and_usascii_strings [ 'bar', - 'foo'.force_encoding('US-ASCII') + 'foo'.dup.force_encoding('US-ASCII') ] end @@ -25,13 +25,13 @@ module ArraySpecs [ 'báz', 'bar', - 'foo'.force_encoding('US-ASCII') + 'foo'.dup.force_encoding('US-ASCII') ] end def self.array_with_usascii_and_utf8_strings [ - 'foo'.force_encoding('US-ASCII'), + 'foo'.dup.force_encoding('US-ASCII'), 'bar', 'báz' ] @@ -41,7 +41,7 @@ module ArraySpecs [ 'bar', 'báz', - 'foo'.force_encoding('BINARY') + 'foo'.dup.force_encoding('BINARY') ] end @@ -55,14 +55,14 @@ module ArraySpecs def self.array_with_usascii_and_7bit_binary_strings [ - 'bar'.force_encoding('US-ASCII'), - 'foo'.force_encoding('BINARY') + 'bar'.dup.force_encoding('US-ASCII'), + 'foo'.dup.force_encoding('BINARY') ] end def self.array_with_usascii_and_binary_strings [ - 'bar'.force_encoding('US-ASCII'), + 'bar'.dup.force_encoding('US-ASCII'), [255].pack('C').force_encoding('BINARY') ] end diff --git a/spec/ruby/core/array/flatten_spec.rb b/spec/ruby/core/array/flatten_spec.rb index 2f9fb8a3ec..8c97000c79 100644 --- a/spec/ruby/core/array/flatten_spec.rb +++ b/spec/ruby/core/array/flatten_spec.rb @@ -75,24 +75,12 @@ describe "Array#flatten" do [[obj]].flatten(1) end - ruby_version_is ''...'3.0' do - it "returns subclass instance for Array subclasses" do - ArraySpecs::MyArray[].flatten.should be_an_instance_of(ArraySpecs::MyArray) - ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(ArraySpecs::MyArray) - ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(ArraySpecs::MyArray) - ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == ArraySpecs::MyArray[1, 2, 3, 4] - [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array) - end - end - - ruby_version_is '3.0' do - it "returns Array instance for Array subclasses" do - ArraySpecs::MyArray[].flatten.should be_an_instance_of(Array) - ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(Array) - ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(Array) - ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == [1, 2, 3, 4] - [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array) - end + it "returns Array instance for Array subclasses" do + ArraySpecs::MyArray[].flatten.should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(Array) + ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(Array) + ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == [1, 2, 3, 4] + [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array) end it "is not destructive" do @@ -147,16 +135,6 @@ describe "Array#flatten" do end end - ruby_version_is ''...'2.7' do - it "returns a tainted array if self is tainted" do - [].taint.flatten.tainted?.should be_true - end - - it "returns an untrusted array if self is untrusted" do - [].untrust.flatten.untrusted?.should be_true - end - end - it "performs respond_to? and method_missing-aware checks when coercing elements to array" do bo = BasicObject.new [bo].flatten.should == [bo] diff --git a/spec/ruby/core/array/initialize_spec.rb b/spec/ruby/core/array/initialize_spec.rb index a8deed2b84..b9fa77b16e 100644 --- a/spec/ruby/core/array/initialize_spec.rb +++ b/spec/ruby/core/array/initialize_spec.rb @@ -53,7 +53,9 @@ describe "Array#initialize with no arguments" do end 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 diff --git a/spec/ruby/core/array/intersect_spec.rb b/spec/ruby/core/array/intersect_spec.rb index b8c5b1e69a..62ac157278 100644 --- a/spec/ruby/core/array/intersect_spec.rb +++ b/spec/ruby/core/array/intersect_spec.rb @@ -1,17 +1,66 @@ require_relative '../../spec_helper' +require_relative 'fixtures/classes' describe 'Array#intersect?' do 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 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 describe 'when there are no elements in common between two Arrays' 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 + + 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 diff --git a/spec/ruby/core/array/intersection_spec.rb b/spec/ruby/core/array/intersection_spec.rb index 27d90f1e44..e01a68d389 100644 --- a/spec/ruby/core/array/intersection_spec.rb +++ b/spec/ruby/core/array/intersection_spec.rb @@ -6,16 +6,14 @@ describe "Array#&" do it_behaves_like :array_intersection, :& end -ruby_version_is "2.7" do - describe "Array#intersection" do - it_behaves_like :array_intersection, :intersection +describe "Array#intersection" do + it_behaves_like :array_intersection, :intersection - it "accepts multiple arguments" do - [1, 2, 3, 4].intersection([1, 2, 3], [2, 3, 4]).should == [2, 3] - end + it "accepts multiple arguments" do + [1, 2, 3, 4].intersection([1, 2, 3], [2, 3, 4]).should == [2, 3] + end - it "preserves elements order from original array" do - [1, 2, 3, 4].intersection([3, 2, 1]).should == [1, 2, 3] - end + it "preserves elements order from original array" do + [1, 2, 3, 4].intersection([3, 2, 1]).should == [1, 2, 3] end end diff --git a/spec/ruby/core/array/keep_if_spec.rb b/spec/ruby/core/array/keep_if_spec.rb index bf2bdeaf91..40f7329b7c 100644 --- a/spec/ruby/core/array/keep_if_spec.rb +++ b/spec/ruby/core/array/keep_if_spec.rb @@ -1,3 +1,4 @@ +require_relative '../../spec_helper' require_relative 'shared/keep_if' describe "Array#keep_if" do diff --git a/spec/ruby/core/array/multiply_spec.rb b/spec/ruby/core/array/multiply_spec.rb index 16e407348b..eca51142fb 100644 --- a/spec/ruby/core/array/multiply_spec.rb +++ b/spec/ruby/core/array/multiply_spec.rb @@ -76,20 +76,10 @@ describe "Array#* with an integer" do @array = ArraySpecs::MyArray[1, 2, 3, 4, 5] end - ruby_version_is ''...'3.0' do - it "returns a subclass instance" do - (@array * 0).should be_an_instance_of(ArraySpecs::MyArray) - (@array * 1).should be_an_instance_of(ArraySpecs::MyArray) - (@array * 2).should be_an_instance_of(ArraySpecs::MyArray) - end - end - - ruby_version_is '3.0' do - it "returns an Array instance" do - (@array * 0).should be_an_instance_of(Array) - (@array * 1).should be_an_instance_of(Array) - (@array * 2).should be_an_instance_of(Array) - end + it "returns an Array instance" do + (@array * 0).should be_an_instance_of(Array) + (@array * 1).should be_an_instance_of(Array) + (@array * 2).should be_an_instance_of(Array) end it "does not call #initialize on the subclass instance" do @@ -97,46 +87,6 @@ describe "Array#* with an integer" do ScratchPad.recorded.should be_nil end end - - ruby_version_is ''...'2.7' do - it "copies the taint status of the original array even if the passed count is 0" do - ary = [1, 2, 3] - ary.taint - (ary * 0).should.tainted? - end - - it "copies the taint status of the original array even if the array is empty" do - ary = [] - ary.taint - (ary * 3).should.tainted? - end - - it "copies the taint status of the original array if the passed count is not 0" do - ary = [1, 2, 3] - ary.taint - (ary * 1).should.tainted? - (ary * 2).should.tainted? - end - - it "copies the untrusted status of the original array even if the passed count is 0" do - ary = [1, 2, 3] - ary.untrust - (ary * 0).should.untrusted? - end - - it "copies the untrusted status of the original array even if the array is empty" do - ary = [] - ary.untrust - (ary * 3).should.untrusted? - end - - it "copies the untrusted status of the original array if the passed count is not 0" do - ary = [1, 2, 3] - ary.untrust - (ary * 1).should.untrusted? - (ary * 2).should.untrusted? - end - end end describe "Array#* with a string" do diff --git a/spec/ruby/core/array/new_spec.rb b/spec/ruby/core/array/new_spec.rb index 96ec6b8198..b50a4857b0 100644 --- a/spec/ruby/core/array/new_spec.rb +++ b/spec/ruby/core/array/new_spec.rb @@ -26,7 +26,9 @@ describe "Array.new with no arguments" do end 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 diff --git a/spec/ruby/core/array/none_spec.rb b/spec/ruby/core/array/none_spec.rb new file mode 100644 index 0000000000..31cd8c46d6 --- /dev/null +++ b/spec/ruby/core/array/none_spec.rb @@ -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 diff --git a/spec/ruby/core/array/one_spec.rb b/spec/ruby/core/array/one_spec.rb new file mode 100644 index 0000000000..0c61907881 --- /dev/null +++ b/spec/ruby/core/array/one_spec.rb @@ -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 diff --git a/spec/ruby/core/array/pack/buffer_spec.rb b/spec/ruby/core/array/pack/buffer_spec.rb index ecb40bfd06..f1206efb3e 100644 --- a/spec/ruby/core/array/pack/buffer_spec.rb +++ b/spec/ruby/core/array/pack/buffer_spec.rb @@ -13,13 +13,13 @@ describe "Array#pack with :buffer option" do it "adds result at the end of buffer content" do n = [ 65, 66, 67 ] # result without buffer is "ABC" - buffer = "" + buffer = +"" n.pack("ccc", buffer: buffer).should == "ABC" - buffer = "123" + buffer = +"123" n.pack("ccc", buffer: buffer).should == "123ABC" - buffer = "12345" + buffer = +"12345" n.pack("ccc", buffer: buffer).should == "12345ABC" end @@ -31,19 +31,19 @@ describe "Array#pack with :buffer option" do context "offset (@) is specified" do it 'keeps buffer content if it is longer than offset' do n = [ 65, 66, 67 ] - buffer = "123456" + buffer = +"123456" n.pack("@3ccc", buffer: buffer).should == "123ABC" end it "fills the gap with \\0 if buffer content is shorter than offset" do n = [ 65, 66, 67 ] - buffer = "123" + buffer = +"123" n.pack("@6ccc", buffer: buffer).should == "123\0\0\0ABC" end it 'does not keep buffer content if it is longer than offset + result' do n = [ 65, 66, 67 ] - buffer = "1234567890" + buffer = +"1234567890" n.pack("@3ccc", buffer: buffer).should == "123ABC" end end diff --git a/spec/ruby/core/array/pack/c_spec.rb b/spec/ruby/core/array/pack/c_spec.rb index 7200830331..ac133ff9b6 100644 --- a/spec/ruby/core/array/pack/c_spec.rb +++ b/spec/ruby/core/array/pack/c_spec.rb @@ -45,8 +45,20 @@ describe :array_pack_8bit, shared: true do [1, 2, 3, 4, 5].pack(pack_format('*')).should == "\x01\x02\x03\x04\x05" end - it "ignores NULL bytes between directives" do - [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [1, 2, 3].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/m_spec.rb b/spec/ruby/core/array/pack/m_spec.rb index 2b1a84abca..c6364af12d 100644 --- a/spec/ruby/core/array/pack/m_spec.rb +++ b/spec/ruby/core/array/pack/m_spec.rb @@ -80,8 +80,16 @@ describe "Array#pack with format 'M'" do ].should be_computed_by(:pack, "M") end - it "encodes a tab followed by a newline with an encoded newline" do + it "encodes a tab at the end of a line with an encoded newline" do + ["\t"].pack("M").should == "\t=\n" ["\t\n"].pack("M").should == "\t=\n\n" + ["abc\t\nxyz"].pack("M").should == "abc\t=\n\nxyz=\n" + end + + it "encodes a space at the end of a line with an encoded newline" do + [" "].pack("M").should == " =\n" + [" \n"].pack("M").should == " =\n\n" + ["abc \nxyz"].pack("M").should == "abc =\n\nxyz=\n" end it "encodes 127..255 in hex format" do diff --git a/spec/ruby/core/array/pack/p_spec.rb b/spec/ruby/core/array/pack/p_spec.rb index d7dff8a4da..b023bf9110 100644 --- a/spec/ruby/core/array/pack/p_spec.rb +++ b/spec/ruby/core/array/pack/p_spec.rb @@ -15,18 +15,6 @@ describe "Array#pack with format 'P'" do ["hello"].pack("P").unpack("P5").should == ["hello"] end - ruby_version_is ''...'2.7' do - it "taints the input string" do - input_string = "hello" - [input_string].pack("P") - input_string.tainted?.should be_true - end - - it "does not taint the output string in normal cases" do - ["hello"].pack("P").tainted?.should be_false - end - end - it "with nil gives a null pointer" do [nil].pack("P").unpack("J").should == [0] end @@ -44,18 +32,6 @@ describe "Array#pack with format 'p'" do ["hello"].pack("p").unpack("p").should == ["hello"] end - ruby_version_is ''...'2.7' do - it "taints the input string" do - input_string = "hello" - [input_string].pack("p") - input_string.tainted?.should be_true - end - - it "does not taint the output string in normal cases" do - ["hello"].pack("p").tainted?.should be_false - end - end - it "with nil gives a null pointer" do [nil].pack("p").unpack("J").should == [0] end diff --git a/spec/ruby/core/array/pack/shared/basic.rb b/spec/ruby/core/array/pack/shared/basic.rb index 9061273ad6..5e3eea55f9 100644 --- a/spec/ruby/core/array/pack/shared/basic.rb +++ b/spec/ruby/core/array/pack/shared/basic.rb @@ -27,17 +27,47 @@ describe :array_pack_basic_non_float, shared: true do [@obj, @obj].pack("a \t\n\v\f\r"+pack_format).should be_an_instance_of(String) end + it "ignores comments in the format string" do + # 2 additional directives ('a') are required for the X directive + [@obj, @obj, @obj, @obj].pack("aa #{pack_format} # some comment \n#{pack_format}").should be_an_instance_of(String) + end + + ruby_version_is ""..."3.2" do + it "warns in verbose mode that a directive is unknown" do + # additional directive ('a') is required for the X directive + -> { [@obj, @obj].pack("a R" + pack_format) }.should complain(/unknown pack directive 'R'/, verbose: true) + -> { [@obj, @obj].pack("a 0" + pack_format) }.should complain(/unknown pack directive '0'/, verbose: true) + -> { [@obj, @obj].pack("a :" + pack_format) }.should complain(/unknown pack directive ':'/, verbose: true) + end + end + + ruby_version_is "3.2"..."3.3" do + # https://bugs.ruby-lang.org/issues/19150 + # NOTE: it's just a plan of the Ruby core team + it "warns that a directive is unknown" do + # additional directive ('a') is required for the X directive + -> { [@obj, @obj].pack("a R" + pack_format) }.should complain(/unknown pack directive 'R'/) + -> { [@obj, @obj].pack("a 0" + pack_format) }.should complain(/unknown pack directive '0'/) + -> { [@obj, @obj].pack("a :" + pack_format) }.should complain(/unknown pack directive ':'/) + end + end + + ruby_version_is "3.3" do + # https://bugs.ruby-lang.org/issues/19150 + # NOTE: Added this case just to not forget about the decision in the ticket + it "raise ArgumentError when a directive is unknown" do + # additional directive ('a') is required for the X directive + -> { [@obj, @obj].pack("a R" + pack_format) }.should raise_error(ArgumentError) + -> { [@obj, @obj].pack("a 0" + pack_format) }.should raise_error(ArgumentError) + -> { [@obj, @obj].pack("a :" + pack_format) }.should raise_error(ArgumentError) + end + end + it "calls #to_str to coerce the directives string" do d = mock("pack directive") d.should_receive(:to_str).and_return("x"+pack_format) [@obj, @obj].pack(d).should be_an_instance_of(String) end - - ruby_version_is ''...'2.7' do - it "taints the output string if the format string is tainted" do - [@obj, @obj].pack("x"+pack_format.taint).tainted?.should be_true - end - end end describe :array_pack_basic_float, shared: true do @@ -45,17 +75,15 @@ describe :array_pack_basic_float, shared: true do [9.3, 4.7].pack(" \t\n\v\f\r"+pack_format).should be_an_instance_of(String) end + it "ignores comments in the format string" do + [9.3, 4.7].pack(pack_format + "# some comment \n" + pack_format).should be_an_instance_of(String) + end + it "calls #to_str to coerce the directives string" do d = mock("pack directive") d.should_receive(:to_str).and_return("x"+pack_format) [1.2, 4.7].pack(d).should be_an_instance_of(String) end - - ruby_version_is ''...'2.7' do - it "taints the output string if the format string is tainted" do - [3.2, 2.8].pack("x"+pack_format.taint).tainted?.should be_true - end - end end describe :array_pack_no_platform, shared: true do diff --git a/spec/ruby/core/array/pack/shared/float.rb b/spec/ruby/core/array/pack/shared/float.rb index ba174a071a..1780d7635e 100644 --- a/spec/ruby/core/array/pack/shared/float.rb +++ b/spec/ruby/core/array/pack/shared/float.rb @@ -25,8 +25,20 @@ describe :array_pack_float_le, shared: true do [2.9, 1.4, 8.2].pack(pack_format("*")).should == "\x9a\x999@33\xb3?33\x03A" end - it "ignores NULL bytes between directives" do - [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [5.3, 9.2].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -74,6 +86,11 @@ describe :array_pack_float_be, shared: true do it "converts an Integer to a Float" do [8].pack(pack_format).should == "A\x00\x00\x00" + [bignum_value].pack(pack_format).should == "_\x80\x00\x00" + end + + it "converts a Rational to a Float" do + [Rational(8)].pack(pack_format).should == "A\x00\x00\x00" end it "raises a TypeError if passed a String representation of a floating point number" do @@ -88,8 +105,20 @@ describe :array_pack_float_be, shared: true do [2.9, 1.4, 8.2].pack(pack_format("*")).should == "@9\x99\x9a?\xb333A\x0333" end - it "ignores NULL bytes between directives" do - [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [5.3, 9.2].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -129,6 +158,11 @@ describe :array_pack_double_le, shared: true do it "converts an Integer to a Float" do [8].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\x20@" + [bignum_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xF0C" + end + + it "converts a Rational to a Float" do + [Rational(8)].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00 @" end it "raises a TypeError if passed a String representation of a floating point number" do @@ -143,8 +177,20 @@ describe :array_pack_double_le, shared: true do [2.9, 1.4, 8.2].pack(pack_format("*")).should == "333333\x07@ffffff\xf6?ffffff\x20@" end - it "ignores NULL bytes between directives" do - [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [5.3, 9.2].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -202,8 +248,20 @@ describe :array_pack_double_be, shared: true do [2.9, 1.4, 8.2].pack(pack_format("*")).should == "@\x07333333?\xf6ffffff@\x20ffffff" end - it "ignores NULL bytes between directives" do - [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [5.3, 9.2].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/shared/integer.rb b/spec/ruby/core/array/pack/shared/integer.rb index 6592f85022..fd21b25b19 100644 --- a/spec/ruby/core/array/pack/shared/integer.rb +++ b/spec/ruby/core/array/pack/shared/integer.rb @@ -41,9 +41,21 @@ describe :array_pack_16bit_le, shared: true do str.should == "\x78\x65\xcd\xab\x21\x43" end - it "ignores NULL bytes between directives" do - str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) - str.should == "\x78\x65\xcd\xab" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x78\x65\xcd\xab" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -93,9 +105,21 @@ describe :array_pack_16bit_be, shared: true do str.should == "\x65\x78\xab\xcd\x43\x21" end - it "ignores NULL bytes between directives" do - str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) - str.should == "\x65\x78\xab\xcd" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x65\x78\xab\xcd" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -145,9 +169,21 @@ describe :array_pack_32bit_le, shared: true do str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78" end - it "ignores NULL bytes between directives" do - str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) - str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -197,9 +233,21 @@ describe :array_pack_32bit_be, shared: true do str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21" end - it "ignores NULL bytes between directives" do - str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) - str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -309,9 +357,21 @@ describe :array_pack_64bit_le, shared: true do str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" end - it "ignores NULL bytes between directives" do - str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) - str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -369,9 +429,21 @@ describe :array_pack_64bit_be, shared: true do str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" end - it "ignores NULL bytes between directives" do - str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) - str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/shared/numeric_basic.rb b/spec/ruby/core/array/pack/shared/numeric_basic.rb index 7c36ba4a32..545e215e64 100644 --- a/spec/ruby/core/array/pack/shared/numeric_basic.rb +++ b/spec/ruby/core/array/pack/shared/numeric_basic.rb @@ -37,8 +37,14 @@ describe :array_pack_float, shared: true do -> { ["a"].pack(pack_format) }.should raise_error(TypeError) end - it "raises a TypeError when the object does not respond to #to_f" do - obj = mock('not an float') + it "raises a TypeError when the object is not Numeric" do + obj = Object.new + -> { [obj].pack(pack_format) }.should raise_error(TypeError, /can't convert Object into Float/) + end + + it "raises a TypeError when the Numeric object does not respond to #to_f" do + klass = Class.new(Numeric) + obj = klass.new -> { [obj].pack(pack_format) }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/array/pack/shared/string.rb b/spec/ruby/core/array/pack/shared/string.rb index 8c82e8c617..2f70dc3951 100644 --- a/spec/ruby/core/array/pack/shared/string.rb +++ b/spec/ruby/core/array/pack/shared/string.rb @@ -40,7 +40,7 @@ describe :array_pack_string, shared: true do f = pack_format("*") [ [["\u{3042 3044 3046 3048}", 0x2000B].pack(f+"U"), Encoding::BINARY], [["abcde\xd1", "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY], - [["a".force_encoding("ascii"), "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY], + [["a".dup.force_encoding("ascii"), "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY], # under discussion [ruby-dev:37294] [["\u{3042 3044 3046 3048}", 1].pack(f+"N"), Encoding::BINARY] ].should be_computed_by(:encoding) diff --git a/spec/ruby/core/array/pack/shared/taint.rb b/spec/ruby/core/array/pack/shared/taint.rb index 565f04b8b9..2c2b011c34 100644 --- a/spec/ruby/core/array/pack/shared/taint.rb +++ b/spec/ruby/core/array/pack/shared/taint.rb @@ -1,35 +1,2 @@ describe :array_pack_taint, shared: true do - ruby_version_is ''...'2.7' do - it "returns a tainted string when a pack argument is tainted" do - ["abcd".taint, 0x20].pack(pack_format("3C")).tainted?.should be_true - end - - it "does not return a tainted string when the array is tainted" do - ["abcd", 0x20].taint.pack(pack_format("3C")).tainted?.should be_false - end - - it "returns a tainted string when the format is tainted" do - ["abcd", 0x20].pack(pack_format("3C").taint).tainted?.should be_true - end - - it "returns a tainted string when an empty format is tainted" do - ["abcd", 0x20].pack("".taint).tainted?.should be_true - end - - it "returns a untrusted string when the format is untrusted" do - ["abcd", 0x20].pack(pack_format("3C").untrust).untrusted?.should be_true - end - - it "returns a untrusted string when the empty format is untrusted" do - ["abcd", 0x20].pack("".untrust).untrusted?.should be_true - end - - it "returns a untrusted string when a pack argument is untrusted" do - ["abcd".untrust, 0x20].pack(pack_format("3C")).untrusted?.should be_true - end - - it "returns a trusted string when the array is untrusted" do - ["abcd", 0x20].untrust.pack(pack_format("3C")).untrusted?.should be_false - end - end end diff --git a/spec/ruby/core/array/pack/shared/unicode.rb b/spec/ruby/core/array/pack/shared/unicode.rb index dd0f8b38aa..4d8eaef323 100644 --- a/spec/ruby/core/array/pack/shared/unicode.rb +++ b/spec/ruby/core/array/pack/shared/unicode.rb @@ -67,8 +67,20 @@ describe :array_pack_unicode, shared: true do -> { [obj].pack("U") }.should raise_error(TypeError) end - it "ignores NULL bytes between directives" do - [1, 2, 3].pack("U\x00U").should == "\x01\x02" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + [1, 2, 3].pack("U\x00U").should == "\x01\x02" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [1, 2, 3].pack("U\x00U") + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/w_spec.rb b/spec/ruby/core/array/pack/w_spec.rb index 439fa02198..48ed4496a5 100644 --- a/spec/ruby/core/array/pack/w_spec.rb +++ b/spec/ruby/core/array/pack/w_spec.rb @@ -24,8 +24,20 @@ describe "Array#pack with format 'w'" do [obj].pack("w").should == "\x05" end - it "ignores NULL bytes between directives" do - [1, 2, 3].pack("w\x00w").should == "\x01\x02" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + suppress_warning do + [1, 2, 3].pack("w\x00w").should == "\x01\x02" + end + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [1, 2, 3].pack("w\x00w") + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/x_spec.rb b/spec/ruby/core/array/pack/x_spec.rb index a28dd0bf21..86c3ad1aa4 100644 --- a/spec/ruby/core/array/pack/x_spec.rb +++ b/spec/ruby/core/array/pack/x_spec.rb @@ -30,6 +30,7 @@ describe "Array#pack with format 'x'" do it "does not add a NULL byte when passed the '*' modifier" do [].pack("x*").should == "" + [1, 2].pack("Cx*C").should == "\x01\x02" end end diff --git a/spec/ruby/core/array/plus_spec.rb b/spec/ruby/core/array/plus_spec.rb index 45f8438208..635bd131c9 100644 --- a/spec/ruby/core/array/plus_spec.rb +++ b/spec/ruby/core/array/plus_spec.rb @@ -14,10 +14,23 @@ describe "Array#+" do (ary + ary).should == [1, 2, 3, 1, 2, 3] end - it "tries to convert the passed argument to an Array using #to_ary" do - obj = mock('["x", "y"]') - obj.should_receive(:to_ary).and_return(["x", "y"]) - ([1, 2, 3] + obj).should == [1, 2, 3, "x", "y"] + describe "converts the passed argument to an Array using #to_ary" do + it "successfully concatenates the resulting array from the #to_ary call" do + obj = mock('["x", "y"]') + obj.should_receive(:to_ary).and_return(["x", "y"]) + ([1, 2, 3] + obj).should == [1, 2, 3, "x", "y"] + end + + it "raises a Typeerror if the given argument can't be converted to an array" do + -> { [1, 2, 3] + nil }.should raise_error(TypeError) + -> { [1, 2, 3] + "abc" }.should raise_error(TypeError) + end + + it "raises a NoMethodError if the given argument raises a NoMethodError during type coercion to an Array" do + obj = mock("hello") + obj.should_receive(:to_ary).and_raise(NoMethodError) + -> { [1, 2, 3] + obj }.should raise_error(NoMethodError) + end end it "properly handles recursive arrays" do @@ -40,20 +53,4 @@ describe "Array#+" do it "does not call to_ary on array subclasses" do ([5, 6] + ArraySpecs::ToAryArray[1, 2]).should == [5, 6, 1, 2] end - - ruby_version_is ''...'2.7' do - it "does not get infected even if an original array is tainted" do - ([1, 2] + [3, 4]).tainted?.should be_false - ([1, 2].taint + [3, 4]).tainted?.should be_false - ([1, 2] + [3, 4].taint).tainted?.should be_false - ([1, 2].taint + [3, 4].taint).tainted?.should be_false - end - - it "does not infected even if an original array is untrusted" do - ([1, 2] + [3, 4]).untrusted?.should be_false - ([1, 2].untrust + [3, 4]).untrusted?.should be_false - ([1, 2] + [3, 4].untrust).untrusted?.should be_false - ([1, 2].untrust + [3, 4].untrust).untrusted?.should be_false - end - end end diff --git a/spec/ruby/core/array/pop_spec.rb b/spec/ruby/core/array/pop_spec.rb index 96ef78da32..2a19408660 100644 --- a/spec/ruby/core/array/pop_spec.rb +++ b/spec/ruby/core/array/pop_spec.rb @@ -30,16 +30,6 @@ describe "Array#pop" do array.pop.should == [1, 'two', 3.0, array, array, array, array] end - ruby_version_is ''...'2.7' do - it "keeps taint status" do - a = [1, 2].taint - a.pop - a.tainted?.should be_true - a.pop - a.tainted?.should be_true - end - end - it "raises a FrozenError on a frozen array" do -> { ArraySpecs.frozen_array.pop }.should raise_error(FrozenError) end @@ -48,16 +38,6 @@ describe "Array#pop" do -> { ArraySpecs.empty_frozen_array.pop }.should raise_error(FrozenError) end - ruby_version_is ''...'2.7' do - it "keeps untrusted status" do - a = [1, 2].untrust - a.pop - a.untrusted?.should be_true - a.pop - a.untrusted?.should be_true - end - end - describe "passed a number n as an argument" do it "removes and returns an array with the last n elements of the array" do a = [1, 2, 3, 4, 5, 6] @@ -136,41 +116,9 @@ describe "Array#pop" do ArraySpecs::MyArray[1, 2, 3].pop(2).should be_an_instance_of(Array) end - ruby_version_is ''...'2.7' do - it "returns an untainted array even if the array is tainted" do - ary = [1, 2].taint - ary.pop(2).tainted?.should be_false - ary.pop(0).tainted?.should be_false - end - - it "keeps taint status" do - a = [1, 2].taint - a.pop(2) - a.tainted?.should be_true - a.pop(2) - a.tainted?.should be_true - end - - it "returns a trusted array even if the array is untrusted" do - ary = [1, 2].untrust - ary.pop(2).untrusted?.should be_false - ary.pop(0).untrusted?.should be_false - end - end - it "raises a FrozenError on a frozen array" do -> { ArraySpecs.frozen_array.pop(2) }.should raise_error(FrozenError) -> { ArraySpecs.frozen_array.pop(0) }.should raise_error(FrozenError) end - - ruby_version_is ''...'2.7' do - it "keeps untrusted status" do - a = [1, 2].untrust - a.pop(2) - a.untrusted?.should be_true - a.pop(2) - a.untrusted?.should be_true - end - end end end diff --git a/spec/ruby/core/array/product_spec.rb b/spec/ruby/core/array/product_spec.rb index 07d2880a96..6fb3818508 100644 --- a/spec/ruby/core/array/product_spec.rb +++ b/spec/ruby/core/array/product_spec.rb @@ -9,6 +9,11 @@ describe "Array#product" do ar.called.should == :to_ary end + it "returns converted arguments using :method_missing" do + ar = ArraySpecs::ArrayMethodMissing.new(2,3) + [1].product(ar).should == [[1,2],[1,3]] + end + it "returns the expected result" do [1,2].product([3,4,5],[6,8]).should == [[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8], [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]] diff --git a/spec/ruby/core/array/rassoc_spec.rb b/spec/ruby/core/array/rassoc_spec.rb index 62fbd40611..632a05e8b3 100644 --- a/spec/ruby/core/array/rassoc_spec.rb +++ b/spec/ruby/core/array/rassoc_spec.rb @@ -35,4 +35,18 @@ describe "Array#rassoc" do [[1, :foobar, o], [2, o, 1], [3, mock('foo')]].rassoc(key).should == [2, o, 1] end + + ruby_version_is "3.3" do + it "calls to_ary on non-array elements" do + s1 = [1, 2] + s2 = ArraySpecs::ArrayConvertible.new(2, 3) + a = [s1, s2] + + s1.should_not_receive(:to_ary) + a.rassoc(2).should equal(s1) + + a.rassoc(3).should == [2, 3] + s2.called.should equal(:to_ary) + end + end end diff --git a/spec/ruby/core/array/reject_spec.rb b/spec/ruby/core/array/reject_spec.rb index fcf43fabde..81a467e364 100644 --- a/spec/ruby/core/array/reject_spec.rb +++ b/spec/ruby/core/array/reject_spec.rb @@ -2,6 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/enumeratorize' require_relative 'shared/delete_if' +require_relative 'shared/iterable_and_tolerating_size_increasing' require_relative '../enumerable/shared/enumeratorized' describe "Array#reject" do @@ -47,6 +48,10 @@ describe "Array#reject" do it_behaves_like :enumeratorized_with_origin_size, :reject, [1,2,3] end +describe "Array#reject" do + it_behaves_like :array_iterable_and_tolerating_size_increasing, :reject +end + describe "Array#reject!" do it "removes elements for which block is true" do 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) 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 a = [1, 2, 3] begin @@ -141,3 +151,8 @@ describe "Array#reject!" do it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3] it_behaves_like :delete_if, :reject! end + +describe "Array#reject!" do + @value_to_return = -> _ { false } + it_behaves_like :array_iterable_and_tolerating_size_increasing, :reject! +end diff --git a/spec/ruby/core/array/reverse_each_spec.rb b/spec/ruby/core/array/reverse_each_spec.rb index 28b8bfcb34..59dabcd33d 100644 --- a/spec/ruby/core/array/reverse_each_spec.rb +++ b/spec/ruby/core/array/reverse_each_spec.rb @@ -5,7 +5,7 @@ require_relative '../enumerable/shared/enumeratorized' # Modifying a collection while the contents are being iterated # gives undefined behavior. See -# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633 +# https://blade.ruby-lang.org/ruby-core/23633 describe "Array#reverse_each" do before :each do @@ -38,6 +38,20 @@ describe "Array#reverse_each" do [1, 2, 3].reverse_each.size.should == 3 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 :enumeratorized_with_origin_size, :reverse_each, [1,2,3] end diff --git a/spec/ruby/core/array/rindex_spec.rb b/spec/ruby/core/array/rindex_spec.rb index 175c7bcfe2..13de88818c 100644 --- a/spec/ruby/core/array/rindex_spec.rb +++ b/spec/ruby/core/array/rindex_spec.rb @@ -4,7 +4,7 @@ require_relative '../enumerable/shared/enumeratorized' # Modifying a collection while the contents are being iterated # gives undefined behavior. See -# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633 +# https://blade.ruby-lang.org/ruby-core/23633 describe "Array#rindex" do it "returns the first index backwards from the end where element == to object" do @@ -68,6 +68,21 @@ describe "Array#rindex" do seen.should == [3] 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 it "produces an Enumerator" do enum = [4, 2, 1, 5, 1, 3].rindex diff --git a/spec/ruby/core/array/sample_spec.rb b/spec/ruby/core/array/sample_spec.rb index 565d7ff7f9..6ef78594f0 100644 --- a/spec/ruby/core/array/sample_spec.rb +++ b/spec/ruby/core/array/sample_spec.rb @@ -3,26 +3,36 @@ require_relative 'fixtures/classes' describe "Array#sample" do it "samples evenly" do - ary = [0, 1, 2, 3] - 3.times do |i| - counts = [0, 0, 0, 0] - 4000.times do - counts[ary.sample(3)[i]] += 1 - end - counts.each do |count| - (800..1200).should include(count) - end - end + ArraySpecs.measure_sample_fairness(4, 1, 400) + ArraySpecs.measure_sample_fairness(4, 2, 400) + ArraySpecs.measure_sample_fairness(4, 3, 400) + ArraySpecs.measure_sample_fairness(40, 3, 400) + ArraySpecs.measure_sample_fairness(40, 4, 400) + ArraySpecs.measure_sample_fairness(40, 8, 400) + ArraySpecs.measure_sample_fairness(40, 16, 400) + ArraySpecs.measure_sample_fairness_large_sample_size(100, 80, 4000) end it "returns nil for an empty Array" do [].sample.should be_nil end + it "returns nil for an empty array when called without n and a Random is given" do + [].sample(random: Random.new(42)).should be_nil + end + it "returns a single value when not passed a count" do [4].sample.should equal(4) end + it "returns a single value when not passed a count and a Random is given" do + [4].sample(random: Random.new(42)).should equal(4) + end + + it "returns a single value when not passed a count and a Random class is given" do + [4].sample(random: Random).should equal(4) + end + it "returns an empty Array when passed zero" do [4].sample(0).should == [] end diff --git a/spec/ruby/core/array/shared/clone.rb b/spec/ruby/core/array/shared/clone.rb index 3c17b1f10f..035b45ec99 100644 --- a/spec/ruby/core/array/shared/clone.rb +++ b/spec/ruby/core/array/shared/clone.rb @@ -17,28 +17,4 @@ describe :array_clone, shared: true do b.should == a b.__id__.should_not == a.__id__ end - - ruby_version_is ''...'2.7' do - it "copies taint status from the original" do - a = [1, 2, 3, 4] - b = [1, 2, 3, 4] - a.taint - aa = a.send @method - bb = b.send @method - - aa.should.tainted? - bb.should_not.tainted? - end - - it "copies untrusted status from the original" do - a = [1, 2, 3, 4] - b = [1, 2, 3, 4] - a.untrust - aa = a.send @method - bb = b.send @method - - aa.should.untrusted? - bb.should_not.untrusted? - end - end end diff --git a/spec/ruby/core/array/shared/collect.rb b/spec/ruby/core/array/shared/collect.rb index d84432734a..030302ced6 100644 --- a/spec/ruby/core/array/shared/collect.rb +++ b/spec/ruby/core/array/shared/collect.rb @@ -1,4 +1,5 @@ require_relative '../../enumerable/shared/enumeratorized' +require_relative '../shared/iterable_and_tolerating_size_increasing' describe :array_collect, shared: true do it "returns a copy of array with each element replaced by the value returned by block" do @@ -42,24 +43,12 @@ describe :array_collect, shared: true do }.should raise_error(ArgumentError) end - ruby_version_is ''...'2.7' do - it "does not copy tainted status" do - a = [1, 2, 3] - a.taint - a.send(@method){|x| x}.tainted?.should be_false - end - - it "does not copy untrusted status" do - a = [1, 2, 3] - a.untrust - a.send(@method){|x| x}.untrusted?.should be_false - end - end - before :all do @object = [1, 2, 3, 4] end it_should_behave_like :enumeratorized_with_origin_size + + it_should_behave_like :array_iterable_and_tolerating_size_increasing end describe :array_collect_b, shared: true do @@ -96,23 +85,6 @@ describe :array_collect_b, shared: true do a.should == ["1!", "2!", "3!"] end - ruby_version_is ''...'2.7' do - it "keeps tainted status" do - a = [1, 2, 3] - a.taint - a.tainted?.should be_true - a.send(@method){|x| x} - a.tainted?.should be_true - end - - it "keeps untrusted status" do - a = [1, 2, 3] - a.untrust - a.send(@method){|x| x} - a.untrusted?.should be_true - end - end - describe "when frozen" do it "raises a FrozenError" do -> { ArraySpecs.frozen_array.send(@method) {} }.should raise_error(FrozenError) @@ -133,8 +105,37 @@ describe :array_collect_b, shared: true do 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 @object = [1, 2, 3, 4] end it_should_behave_like :enumeratorized_with_origin_size + + it_should_behave_like :array_iterable_and_tolerating_size_increasing end diff --git a/spec/ruby/core/array/shared/index.rb b/spec/ruby/core/array/shared/index.rb index a9896554f2..a4a0adbab6 100644 --- a/spec/ruby/core/array/shared/index.rb +++ b/spec/ruby/core/array/shared/index.rb @@ -1,3 +1,5 @@ +require_relative '../shared/iterable_and_tolerating_size_increasing' + describe :array_index, shared: true do it "returns the index of the first element == to object" do x = mock('3') @@ -34,4 +36,6 @@ describe :array_index, shared: true do [].send(@method).should be_an_instance_of(Enumerator) end end + + it_should_behave_like :array_iterable_and_tolerating_size_increasing end diff --git a/spec/ruby/core/array/shared/inspect.rb b/spec/ruby/core/array/shared/inspect.rb index 736f8d946b..af5128c645 100644 --- a/spec/ruby/core/array/shared/inspect.rb +++ b/spec/ruby/core/array/shared/inspect.rb @@ -19,7 +19,7 @@ describe :array_inspect, shared: true do end it "does not call #to_s on a String returned from #inspect" do - str = "abc" + str = +"abc" str.should_not_receive(:to_s) [str].send(@method).should == '["abc"]' @@ -64,32 +64,6 @@ describe :array_inspect, shared: true do ArraySpecs.empty_recursive_array.send(@method).should == "[[...]]" end - ruby_version_is ''...'2.7' do - it "taints the result if the Array is non-empty and tainted" do - [1, 2].taint.send(@method).tainted?.should be_true - end - - it "does not taint the result if the Array is tainted but empty" do - [].taint.send(@method).tainted?.should be_false - end - - it "taints the result if an element is tainted" do - ["str".taint].send(@method).tainted?.should be_true - end - - it "untrusts the result if the Array is untrusted" do - [1, 2].untrust.send(@method).untrusted?.should be_true - end - - it "does not untrust the result if the Array is untrusted but empty" do - [].untrust.send(@method).untrusted?.should be_false - end - - it "untrusts the result if an element is untrusted" do - ["str".untrust].send(@method).untrusted?.should be_true - end - end - describe "with encoding" do before :each do @default_external_encoding = Encoding.default_external @@ -124,8 +98,8 @@ describe :array_inspect, shared: true do end it "does not raise if inspected result is not default external encoding" do - utf_16be = mock("utf_16be") - utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE)) + utf_16be = mock(+"utf_16be") + utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE)) [utf_16be].send(@method).should == '["utf_16be \u3042"]' end diff --git a/spec/ruby/core/array/shared/intersection.rb b/spec/ruby/core/array/shared/intersection.rb index 49849b08c2..0b4166ab63 100644 --- a/spec/ruby/core/array/shared/intersection.rb +++ b/spec/ruby/core/array/shared/intersection.rb @@ -11,7 +11,8 @@ describe :array_intersection, shared: true do 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] + [ 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 it "does not modify the original Array" do diff --git a/spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb b/spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb new file mode 100644 index 0000000000..3e73bad44b --- /dev/null +++ b/spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb @@ -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 diff --git a/spec/ruby/core/array/shared/join.rb b/spec/ruby/core/array/shared/join.rb index dfdb4ae1e4..507b13e3c8 100644 --- a/spec/ruby/core/array/shared/join.rb +++ b/spec/ruby/core/array/shared/join.rb @@ -58,36 +58,6 @@ describe :array_join_with_default_separator, shared: true do -> { ArraySpecs.empty_recursive_array.send(@method) }.should raise_error(ArgumentError) end - ruby_version_is ''...'2.7' do - it "taints the result if the Array is tainted and non-empty" do - [1, 2].taint.send(@method).tainted?.should be_true - end - - it "does not taint the result if the Array is tainted but empty" do - [].taint.send(@method).tainted?.should be_false - end - - it "taints the result if the result of coercing an element is tainted" do - s = mock("taint") - s.should_receive(:to_s).and_return("str".taint) - [s].send(@method).tainted?.should be_true - end - - it "untrusts the result if the Array is untrusted and non-empty" do - [1, 2].untrust.send(@method).untrusted?.should be_true - end - - it "does not untrust the result if the Array is untrusted but empty" do - [].untrust.send(@method).untrusted?.should be_false - end - - it "untrusts the result if the result of coercing an element is untrusted" do - s = mock("untrust") - s.should_receive(:to_s).and_return("str".untrust) - [s].send(@method).untrusted?.should be_true - end - end - it "uses the first encoding when other strings are compatible" do ary1 = ArraySpecs.array_with_7bit_utf8_and_usascii_strings ary2 = ArraySpecs.array_with_usascii_and_7bit_utf8_strings @@ -114,18 +84,16 @@ describe :array_join_with_default_separator, shared: true do -> { ary_utf8_bad_binary.send(@method) }.should raise_error(EncodingError) end - ruby_version_is "2.7" do - context "when $, is not nil" do - before do - suppress_warning do - $, = '*' - end + 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/) - -> { [].join(nil) }.should complain(/warning: \$, is set to non-nil value/) - end + it "warns" do + -> { [].join }.should complain(/warning: \$, is set to non-nil value/) + -> { [].join(nil) }.should complain(/warning: \$, is set to non-nil value/) end end end @@ -141,42 +109,4 @@ describe :array_join_with_string_separator, shared: true do [1, [2, [3, 4], 5], 6].send(@method, ":").should == "1:2:3:4:5:6" [1, [2, ArraySpecs::MyArray[3, 4], 5], 6].send(@method, ":").should == "1:2:3:4:5:6" end - - ruby_version_is ''...'2.7' do - describe "with a tainted separator" do - before :each do - @sep = ":".taint - end - - it "does not taint the result if the array is empty" do - [].send(@method, @sep).tainted?.should be_false - end - - it "does not taint the result if the array has only one element" do - [1].send(@method, @sep).tainted?.should be_false - end - - it "taints the result if the array has two or more elements" do - [1, 2].send(@method, @sep).tainted?.should be_true - end - end - - describe "with an untrusted separator" do - before :each do - @sep = ":".untrust - end - - it "does not untrust the result if the array is empty" do - [].send(@method, @sep).untrusted?.should be_false - end - - it "does not untrust the result if the array has only one element" do - [1].send(@method, @sep).untrusted?.should be_false - end - - it "untrusts the result if the array has two or more elements" do - [1, 2].send(@method, @sep).untrusted?.should be_true - end - end - end end diff --git a/spec/ruby/core/array/shared/keep_if.rb b/spec/ruby/core/array/shared/keep_if.rb index f26aff028c..43a047c0a7 100644 --- a/spec/ruby/core/array/shared/keep_if.rb +++ b/spec/ruby/core/array/shared/keep_if.rb @@ -1,4 +1,5 @@ require_relative '../../enumerable/shared/enumeratorized' +require_relative '../shared/iterable_and_tolerating_size_increasing' describe :keep_if, shared: true 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) 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 + + 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 diff --git a/spec/ruby/core/array/shared/select.rb b/spec/ruby/core/array/shared/select.rb index 09101e8ab5..9c2cbf76c4 100644 --- a/spec/ruby/core/array/shared/select.rb +++ b/spec/ruby/core/array/shared/select.rb @@ -2,11 +2,14 @@ require_relative '../../../spec_helper' require_relative '../fixtures/classes' require_relative '../shared/enumeratorize' require_relative '../shared/keep_if' +require_relative '../shared/iterable_and_tolerating_size_increasing' require_relative '../../enumerable/shared/enumeratorized' describe :array_select, shared: true do it_should_behave_like :enumeratorize + it_should_behave_like :array_iterable_and_tolerating_size_increasing + before :each do @object = [1,2,3] end diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb index 540a050130..d2866970a5 100644 --- a/spec/ruby/core/array/shared/slice.rb +++ b/spec/ruby/core/array/shared/slice.rb @@ -397,56 +397,28 @@ describe :array_slice, shared: true do @array = ArraySpecs::MyArray[1, 2, 3, 4, 5] end - ruby_version_is ''...'3.0' do - it "returns a subclass instance with [n, m]" do - @array.send(@method, 0, 2).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [-n, m]" do - @array.send(@method, -3, 2).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [n..m]" do - @array.send(@method, 1..3).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [n...m]" do - @array.send(@method, 1...3).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [-n..-m]" do - @array.send(@method, -3..-1).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [-n...-m]" do - @array.send(@method, -3...-1).should be_an_instance_of(ArraySpecs::MyArray) - end + it "returns a Array instance with [n, m]" do + @array.send(@method, 0, 2).should be_an_instance_of(Array) end - ruby_version_is '3.0' do - it "returns a Array instance with [n, m]" do - @array.send(@method, 0, 2).should be_an_instance_of(Array) - end - - it "returns a Array instance with [-n, m]" do - @array.send(@method, -3, 2).should be_an_instance_of(Array) - end + it "returns a Array instance with [-n, m]" do + @array.send(@method, -3, 2).should be_an_instance_of(Array) + end - it "returns a Array instance with [n..m]" do - @array.send(@method, 1..3).should be_an_instance_of(Array) - end + it "returns a Array instance with [n..m]" do + @array.send(@method, 1..3).should be_an_instance_of(Array) + end - it "returns a Array instance with [n...m]" do - @array.send(@method, 1...3).should be_an_instance_of(Array) - end + it "returns a Array instance with [n...m]" do + @array.send(@method, 1...3).should be_an_instance_of(Array) + end - it "returns a Array instance with [-n..-m]" do - @array.send(@method, -3..-1).should be_an_instance_of(Array) - end + it "returns a Array instance with [-n..-m]" do + @array.send(@method, -3..-1).should be_an_instance_of(Array) + end - it "returns a Array instance with [-n...-m]" do - @array.send(@method, -3...-1).should be_an_instance_of(Array) - end + it "returns a Array instance with [-n...-m]" do + @array.send(@method, -3...-1).should be_an_instance_of(Array) end it "returns an empty array when m == n with [m...n]" do @@ -534,262 +506,354 @@ describe :array_slice, shared: true do a.send(@method, eval("(-9...)")).should == nil end - ruby_version_is "3.0" do - describe "can be sliced with Enumerator::ArithmeticSequence" do - before :each do - @array = [0, 1, 2, 3, 4, 5] - end + describe "can be sliced with Enumerator::ArithmeticSequence" do + before :each do + @array = [0, 1, 2, 3, 4, 5] + end - it "has endless range and positive steps" do - @array.send(@method, eval("(0..).step(1)")).should == [0, 1, 2, 3, 4, 5] - @array.send(@method, eval("(0..).step(2)")).should == [0, 2, 4] - @array.send(@method, eval("(0..).step(10)")).should == [0] + it "has endless range and positive steps" do + @array.send(@method, eval("(0..).step(1)")).should == [0, 1, 2, 3, 4, 5] + @array.send(@method, eval("(0..).step(2)")).should == [0, 2, 4] + @array.send(@method, eval("(0..).step(10)")).should == [0] - @array.send(@method, eval("(2..).step(1)")).should == [2, 3, 4, 5] - @array.send(@method, eval("(2..).step(2)")).should == [2, 4] - @array.send(@method, eval("(2..).step(10)")).should == [2] + @array.send(@method, eval("(2..).step(1)")).should == [2, 3, 4, 5] + @array.send(@method, eval("(2..).step(2)")).should == [2, 4] + @array.send(@method, eval("(2..).step(10)")).should == [2] - @array.send(@method, eval("(-3..).step(1)")).should == [3, 4, 5] - @array.send(@method, eval("(-3..).step(2)")).should == [3, 5] - @array.send(@method, eval("(-3..).step(10)")).should == [3] - end + @array.send(@method, eval("(-3..).step(1)")).should == [3, 4, 5] + @array.send(@method, eval("(-3..).step(2)")).should == [3, 5] + @array.send(@method, eval("(-3..).step(10)")).should == [3] + end - it "has beginless range and positive steps" do - # end with zero index - @array.send(@method, eval("(..0).step(1)")).should == [0] - @array.send(@method, eval("(...0).step(1)")).should == [] + it "has beginless range and positive steps" do + # end with zero index + @array.send(@method, (..0).step(1)).should == [0] + @array.send(@method, (...0).step(1)).should == [] - @array.send(@method, eval("(..0).step(2)")).should == [0] - @array.send(@method, eval("(...0).step(2)")).should == [] + @array.send(@method, (..0).step(2)).should == [0] + @array.send(@method, (...0).step(2)).should == [] - @array.send(@method, eval("(..0).step(10)")).should == [0] - @array.send(@method, eval("(...0).step(10)")).should == [] + @array.send(@method, (..0).step(10)).should == [0] + @array.send(@method, (...0).step(10)).should == [] - # end with positive index - @array.send(@method, eval("(..3).step(1)")).should == [0, 1, 2, 3] - @array.send(@method, eval("(...3).step(1)")).should == [0, 1, 2] + # end with positive index + @array.send(@method, (..3).step(1)).should == [0, 1, 2, 3] + @array.send(@method, (...3).step(1)).should == [0, 1, 2] - @array.send(@method, eval("(..3).step(2)")).should == [0, 2] - @array.send(@method, eval("(...3).step(2)")).should == [0, 2] + @array.send(@method, (..3).step(2)).should == [0, 2] + @array.send(@method, (...3).step(2)).should == [0, 2] - @array.send(@method, eval("(..3).step(10)")).should == [0] - @array.send(@method, eval("(...3).step(10)")).should == [0] + @array.send(@method, (..3).step(10)).should == [0] + @array.send(@method, (...3).step(10)).should == [0] - # end with negative index - @array.send(@method, eval("(..-2).step(1)")).should == [0, 1, 2, 3, 4,] - @array.send(@method, eval("(...-2).step(1)")).should == [0, 1, 2, 3] + # end with negative index + @array.send(@method, (..-2).step(1)).should == [0, 1, 2, 3, 4,] + @array.send(@method, (...-2).step(1)).should == [0, 1, 2, 3] - @array.send(@method, eval("(..-2).step(2)")).should == [0, 2, 4] - @array.send(@method, eval("(...-2).step(2)")).should == [0, 2] + @array.send(@method, (..-2).step(2)).should == [0, 2, 4] + @array.send(@method, (...-2).step(2)).should == [0, 2] - @array.send(@method, eval("(..-2).step(10)")).should == [0] - @array.send(@method, eval("(...-2).step(10)")).should == [0] - end + @array.send(@method, (..-2).step(10)).should == [0] + @array.send(@method, (...-2).step(10)).should == [0] + end - it "has endless range and negative steps" do - @array.send(@method, eval("(0..).step(-1)")).should == [0] - @array.send(@method, eval("(0..).step(-2)")).should == [0] - @array.send(@method, eval("(0..).step(-10)")).should == [0] + it "has endless range and negative steps" do + @array.send(@method, eval("(0..).step(-1)")).should == [0] + @array.send(@method, eval("(0..).step(-2)")).should == [0] + @array.send(@method, eval("(0..).step(-10)")).should == [0] - @array.send(@method, eval("(2..).step(-1)")).should == [2, 1, 0] - @array.send(@method, eval("(2..).step(-2)")).should == [2, 0] + @array.send(@method, eval("(2..).step(-1)")).should == [2, 1, 0] + @array.send(@method, eval("(2..).step(-2)")).should == [2, 0] - @array.send(@method, eval("(-3..).step(-1)")).should == [3, 2, 1, 0] - @array.send(@method, eval("(-3..).step(-2)")).should == [3, 1] - end + @array.send(@method, eval("(-3..).step(-1)")).should == [3, 2, 1, 0] + @array.send(@method, eval("(-3..).step(-2)")).should == [3, 1] + end - it "has closed range and positive steps" do - # start and end with 0 - @array.send(@method, eval("(0..0).step(1)")).should == [0] - @array.send(@method, eval("(0...0).step(1)")).should == [] + it "has closed range and positive steps" do + # start and end with 0 + @array.send(@method, eval("(0..0).step(1)")).should == [0] + @array.send(@method, eval("(0...0).step(1)")).should == [] - @array.send(@method, eval("(0..0).step(2)")).should == [0] - @array.send(@method, eval("(0...0).step(2)")).should == [] + @array.send(@method, eval("(0..0).step(2)")).should == [0] + @array.send(@method, eval("(0...0).step(2)")).should == [] - @array.send(@method, eval("(0..0).step(10)")).should == [0] - @array.send(@method, eval("(0...0).step(10)")).should == [] + @array.send(@method, eval("(0..0).step(10)")).should == [0] + @array.send(@method, eval("(0...0).step(10)")).should == [] - # start and end with positive index - @array.send(@method, eval("(1..3).step(1)")).should == [1, 2, 3] - @array.send(@method, eval("(1...3).step(1)")).should == [1, 2] + # start and end with positive index + @array.send(@method, eval("(1..3).step(1)")).should == [1, 2, 3] + @array.send(@method, eval("(1...3).step(1)")).should == [1, 2] - @array.send(@method, eval("(1..3).step(2)")).should == [1, 3] - @array.send(@method, eval("(1...3).step(2)")).should == [1] + @array.send(@method, eval("(1..3).step(2)")).should == [1, 3] + @array.send(@method, eval("(1...3).step(2)")).should == [1] - @array.send(@method, eval("(1..3).step(10)")).should == [1] - @array.send(@method, eval("(1...3).step(10)")).should == [1] + @array.send(@method, eval("(1..3).step(10)")).should == [1] + @array.send(@method, eval("(1...3).step(10)")).should == [1] - # start with positive index, end with negative index - @array.send(@method, eval("(1..-2).step(1)")).should == [1, 2, 3, 4] - @array.send(@method, eval("(1...-2).step(1)")).should == [1, 2, 3] + # start with positive index, end with negative index + @array.send(@method, eval("(1..-2).step(1)")).should == [1, 2, 3, 4] + @array.send(@method, eval("(1...-2).step(1)")).should == [1, 2, 3] - @array.send(@method, eval("(1..-2).step(2)")).should == [1, 3] - @array.send(@method, eval("(1...-2).step(2)")).should == [1, 3] + @array.send(@method, eval("(1..-2).step(2)")).should == [1, 3] + @array.send(@method, eval("(1...-2).step(2)")).should == [1, 3] - @array.send(@method, eval("(1..-2).step(10)")).should == [1] - @array.send(@method, eval("(1...-2).step(10)")).should == [1] + @array.send(@method, eval("(1..-2).step(10)")).should == [1] + @array.send(@method, eval("(1...-2).step(10)")).should == [1] - # start with negative index, end with positive index - @array.send(@method, eval("(-4..4).step(1)")).should == [2, 3, 4] - @array.send(@method, eval("(-4...4).step(1)")).should == [2, 3] + # start with negative index, end with positive index + @array.send(@method, eval("(-4..4).step(1)")).should == [2, 3, 4] + @array.send(@method, eval("(-4...4).step(1)")).should == [2, 3] - @array.send(@method, eval("(-4..4).step(2)")).should == [2, 4] - @array.send(@method, eval("(-4...4).step(2)")).should == [2] + @array.send(@method, eval("(-4..4).step(2)")).should == [2, 4] + @array.send(@method, eval("(-4...4).step(2)")).should == [2] - @array.send(@method, eval("(-4..4).step(10)")).should == [2] - @array.send(@method, eval("(-4...4).step(10)")).should == [2] + @array.send(@method, eval("(-4..4).step(10)")).should == [2] + @array.send(@method, eval("(-4...4).step(10)")).should == [2] - # start with negative index, end with negative index - @array.send(@method, eval("(-4..-2).step(1)")).should == [2, 3, 4] - @array.send(@method, eval("(-4...-2).step(1)")).should == [2, 3] + # start with negative index, end with negative index + @array.send(@method, eval("(-4..-2).step(1)")).should == [2, 3, 4] + @array.send(@method, eval("(-4...-2).step(1)")).should == [2, 3] - @array.send(@method, eval("(-4..-2).step(2)")).should == [2, 4] - @array.send(@method, eval("(-4...-2).step(2)")).should == [2] + @array.send(@method, eval("(-4..-2).step(2)")).should == [2, 4] + @array.send(@method, eval("(-4...-2).step(2)")).should == [2] - @array.send(@method, eval("(-4..-2).step(10)")).should == [2] - @array.send(@method, eval("(-4...-2).step(10)")).should == [2] - end + @array.send(@method, eval("(-4..-2).step(10)")).should == [2] + @array.send(@method, eval("(-4...-2).step(10)")).should == [2] + end - it "has closed range and negative steps" do - # start and end with 0 - @array.send(@method, eval("(0..0).step(-1)")).should == [0] - @array.send(@method, eval("(0...0).step(-1)")).should == [] + it "has closed range and negative steps" do + # start and end with 0 + @array.send(@method, eval("(0..0).step(-1)")).should == [0] + @array.send(@method, eval("(0...0).step(-1)")).should == [] - @array.send(@method, eval("(0..0).step(-2)")).should == [0] - @array.send(@method, eval("(0...0).step(-2)")).should == [] + @array.send(@method, eval("(0..0).step(-2)")).should == [0] + @array.send(@method, eval("(0...0).step(-2)")).should == [] - @array.send(@method, eval("(0..0).step(-10)")).should == [0] - @array.send(@method, eval("(0...0).step(-10)")).should == [] + @array.send(@method, eval("(0..0).step(-10)")).should == [0] + @array.send(@method, eval("(0...0).step(-10)")).should == [] - # start and end with positive index - @array.send(@method, eval("(1..3).step(-1)")).should == [] - @array.send(@method, eval("(1...3).step(-1)")).should == [] + # start and end with positive index + @array.send(@method, eval("(1..3).step(-1)")).should == [] + @array.send(@method, eval("(1...3).step(-1)")).should == [] - @array.send(@method, eval("(1..3).step(-2)")).should == [] - @array.send(@method, eval("(1...3).step(-2)")).should == [] + @array.send(@method, eval("(1..3).step(-2)")).should == [] + @array.send(@method, eval("(1...3).step(-2)")).should == [] - @array.send(@method, eval("(1..3).step(-10)")).should == [] - @array.send(@method, eval("(1...3).step(-10)")).should == [] + @array.send(@method, eval("(1..3).step(-10)")).should == [] + @array.send(@method, eval("(1...3).step(-10)")).should == [] - # start with positive index, end with negative index - @array.send(@method, eval("(1..-2).step(-1)")).should == [] - @array.send(@method, eval("(1...-2).step(-1)")).should == [] + # start with positive index, end with negative index + @array.send(@method, eval("(1..-2).step(-1)")).should == [] + @array.send(@method, eval("(1...-2).step(-1)")).should == [] - @array.send(@method, eval("(1..-2).step(-2)")).should == [] - @array.send(@method, eval("(1...-2).step(-2)")).should == [] + @array.send(@method, eval("(1..-2).step(-2)")).should == [] + @array.send(@method, eval("(1...-2).step(-2)")).should == [] - @array.send(@method, eval("(1..-2).step(-10)")).should == [] - @array.send(@method, eval("(1...-2).step(-10)")).should == [] + @array.send(@method, eval("(1..-2).step(-10)")).should == [] + @array.send(@method, eval("(1...-2).step(-10)")).should == [] - # start with negative index, end with positive index - @array.send(@method, eval("(-4..4).step(-1)")).should == [] - @array.send(@method, eval("(-4...4).step(-1)")).should == [] + # start with negative index, end with positive index + @array.send(@method, eval("(-4..4).step(-1)")).should == [] + @array.send(@method, eval("(-4...4).step(-1)")).should == [] - @array.send(@method, eval("(-4..4).step(-2)")).should == [] - @array.send(@method, eval("(-4...4).step(-2)")).should == [] + @array.send(@method, eval("(-4..4).step(-2)")).should == [] + @array.send(@method, eval("(-4...4).step(-2)")).should == [] - @array.send(@method, eval("(-4..4).step(-10)")).should == [] - @array.send(@method, eval("(-4...4).step(-10)")).should == [] + @array.send(@method, eval("(-4..4).step(-10)")).should == [] + @array.send(@method, eval("(-4...4).step(-10)")).should == [] - # start with negative index, end with negative index - @array.send(@method, eval("(-4..-2).step(-1)")).should == [] - @array.send(@method, eval("(-4...-2).step(-1)")).should == [] + # start with negative index, end with negative index + @array.send(@method, eval("(-4..-2).step(-1)")).should == [] + @array.send(@method, eval("(-4...-2).step(-1)")).should == [] - @array.send(@method, eval("(-4..-2).step(-2)")).should == [] - @array.send(@method, eval("(-4...-2).step(-2)")).should == [] + @array.send(@method, eval("(-4..-2).step(-2)")).should == [] + @array.send(@method, eval("(-4...-2).step(-2)")).should == [] - @array.send(@method, eval("(-4..-2).step(-10)")).should == [] - @array.send(@method, eval("(-4...-2).step(-10)")).should == [] - end + @array.send(@method, eval("(-4..-2).step(-10)")).should == [] + @array.send(@method, eval("(-4...-2).step(-10)")).should == [] + end - it "has inverted closed range and positive steps" do - # start and end with positive index - @array.send(@method, eval("(3..1).step(1)")).should == [] - @array.send(@method, eval("(3...1).step(1)")).should == [] + it "has inverted closed range and positive steps" do + # start and end with positive index + @array.send(@method, eval("(3..1).step(1)")).should == [] + @array.send(@method, eval("(3...1).step(1)")).should == [] - @array.send(@method, eval("(3..1).step(2)")).should == [] - @array.send(@method, eval("(3...1).step(2)")).should == [] + @array.send(@method, eval("(3..1).step(2)")).should == [] + @array.send(@method, eval("(3...1).step(2)")).should == [] - @array.send(@method, eval("(3..1).step(10)")).should == [] - @array.send(@method, eval("(3...1).step(10)")).should == [] + @array.send(@method, eval("(3..1).step(10)")).should == [] + @array.send(@method, eval("(3...1).step(10)")).should == [] - # start with negative index, end with positive index - @array.send(@method, eval("(-2..1).step(1)")).should == [] - @array.send(@method, eval("(-2...1).step(1)")).should == [] + # start with negative index, end with positive index + @array.send(@method, eval("(-2..1).step(1)")).should == [] + @array.send(@method, eval("(-2...1).step(1)")).should == [] - @array.send(@method, eval("(-2..1).step(2)")).should == [] - @array.send(@method, eval("(-2...1).step(2)")).should == [] + @array.send(@method, eval("(-2..1).step(2)")).should == [] + @array.send(@method, eval("(-2...1).step(2)")).should == [] - @array.send(@method, eval("(-2..1).step(10)")).should == [] - @array.send(@method, eval("(-2...1).step(10)")).should == [] + @array.send(@method, eval("(-2..1).step(10)")).should == [] + @array.send(@method, eval("(-2...1).step(10)")).should == [] - # start with positive index, end with negative index - @array.send(@method, eval("(4..-4).step(1)")).should == [] - @array.send(@method, eval("(4...-4).step(1)")).should == [] + # start with positive index, end with negative index + @array.send(@method, eval("(4..-4).step(1)")).should == [] + @array.send(@method, eval("(4...-4).step(1)")).should == [] - @array.send(@method, eval("(4..-4).step(2)")).should == [] - @array.send(@method, eval("(4...-4).step(2)")).should == [] + @array.send(@method, eval("(4..-4).step(2)")).should == [] + @array.send(@method, eval("(4...-4).step(2)")).should == [] - @array.send(@method, eval("(4..-4).step(10)")).should == [] - @array.send(@method, eval("(4...-4).step(10)")).should == [] + @array.send(@method, eval("(4..-4).step(10)")).should == [] + @array.send(@method, eval("(4...-4).step(10)")).should == [] - # start with negative index, end with negative index - @array.send(@method, eval("(-2..-4).step(1)")).should == [] - @array.send(@method, eval("(-2...-4).step(1)")).should == [] + # start with negative index, end with negative index + @array.send(@method, eval("(-2..-4).step(1)")).should == [] + @array.send(@method, eval("(-2...-4).step(1)")).should == [] - @array.send(@method, eval("(-2..-4).step(2)")).should == [] - @array.send(@method, eval("(-2...-4).step(2)")).should == [] + @array.send(@method, eval("(-2..-4).step(2)")).should == [] + @array.send(@method, eval("(-2...-4).step(2)")).should == [] - @array.send(@method, eval("(-2..-4).step(10)")).should == [] - @array.send(@method, eval("(-2...-4).step(10)")).should == [] - end + @array.send(@method, eval("(-2..-4).step(10)")).should == [] + @array.send(@method, eval("(-2...-4).step(10)")).should == [] + end - it "has range with bounds outside of array" do - # end is equal to array's length - @array.send(@method, (0..6).step(1)).should == [0, 1, 2, 3, 4, 5] - -> { @array.send(@method, (0..6).step(2)) }.should raise_error(RangeError) + it "has range with bounds outside of array" do + # end is equal to array's length + @array.send(@method, (0..6).step(1)).should == [0, 1, 2, 3, 4, 5] + -> { @array.send(@method, (0..6).step(2)) }.should raise_error(RangeError) - # end is greater than length with positive steps - @array.send(@method, (1..6).step(2)).should == [1, 3, 5] - @array.send(@method, (2..7).step(2)).should == [2, 4] - -> { @array.send(@method, (2..8).step(2)) }.should raise_error(RangeError) + # end is greater than length with positive steps + @array.send(@method, (1..6).step(2)).should == [1, 3, 5] + @array.send(@method, (2..7).step(2)).should == [2, 4] + -> { @array.send(@method, (2..8).step(2)) }.should raise_error(RangeError) - # begin is greater than length with negative steps - @array.send(@method, (6..1).step(-2)).should == [5, 3, 1] - @array.send(@method, (7..2).step(-2)).should == [5, 3] - -> { @array.send(@method, (8..2).step(-2)) }.should raise_error(RangeError) - end + # begin is greater than length with negative steps + @array.send(@method, (6..1).step(-2)).should == [5, 3, 1] + @array.send(@method, (7..2).step(-2)).should == [5, 3] + -> { @array.send(@method, (8..2).step(-2)) }.should raise_error(RangeError) + end - it "has endless range with start outside of array's bounds" do - @array.send(@method, eval("(6..).step(1)")).should == [] - @array.send(@method, eval("(7..).step(1)")).should == nil + it "has endless range with start outside of array's bounds" do + @array.send(@method, eval("(6..).step(1)")).should == [] + @array.send(@method, eval("(7..).step(1)")).should == nil - @array.send(@method, eval("(6..).step(2)")).should == [] - -> { @array.send(@method, eval("(7..).step(2)")) }.should raise_error(RangeError) - end + @array.send(@method, eval("(6..).step(2)")).should == [] + -> { @array.send(@method, eval("(7..).step(2)")) }.should raise_error(RangeError) end end - ruby_version_is "2.7" do - it "can accept beginless ranges" do - a = [0, 1, 2, 3, 4, 5] - a.send(@method, eval("(..3)")).should == [0, 1, 2, 3] - a.send(@method, eval("(...3)")).should == [0, 1, 2] - a.send(@method, eval("(..-3)")).should == [0, 1, 2, 3] - a.send(@method, eval("(...-3)")).should == [0, 1, 2] - a.send(@method, eval("(..0)")).should == [0] - a.send(@method, eval("(...0)")).should == [] - a.send(@method, eval("(..9)")).should == [0, 1, 2, 3, 4, 5] - a.send(@method, eval("(...9)")).should == [0, 1, 2, 3, 4, 5] - a.send(@method, eval("(..-9)")).should == [] - a.send(@method, eval("(...-9)")).should == [] + it "can accept beginless ranges" do + a = [0, 1, 2, 3, 4, 5] + a.send(@method, (..3)).should == [0, 1, 2, 3] + a.send(@method, (...3)).should == [0, 1, 2] + a.send(@method, (..-3)).should == [0, 1, 2, 3] + a.send(@method, (...-3)).should == [0, 1, 2] + a.send(@method, (..0)).should == [0] + a.send(@method, (...0)).should == [] + a.send(@method, (..9)).should == [0, 1, 2, 3, 4, 5] + a.send(@method, (...9)).should == [0, 1, 2, 3, 4, 5] + a.send(@method, (..-9)).should == [] + a.send(@method, (...-9)).should == [] + end + + ruby_version_is "3.2" do + describe "can be sliced with Enumerator::ArithmeticSequence" do + it "with infinite/inverted ranges and negative steps" do + @array = [0, 1, 2, 3, 4, 5] + @array.send(@method, (2..).step(-1)).should == [2, 1, 0] + @array.send(@method, (2..).step(-2)).should == [2, 0] + @array.send(@method, (2..).step(-3)).should == [2] + @array.send(@method, (2..).step(-4)).should == [2] + + @array.send(@method, (-3..).step(-1)).should == [3, 2, 1, 0] + @array.send(@method, (-3..).step(-2)).should == [3, 1] + @array.send(@method, (-3..).step(-3)).should == [3, 0] + @array.send(@method, (-3..).step(-4)).should == [3] + @array.send(@method, (-3..).step(-5)).should == [3] + + @array.send(@method, (..0).step(-1)).should == [5, 4, 3, 2, 1, 0] + @array.send(@method, (..0).step(-2)).should == [5, 3, 1] + @array.send(@method, (..0).step(-3)).should == [5, 2] + @array.send(@method, (..0).step(-4)).should == [5, 1] + @array.send(@method, (..0).step(-5)).should == [5, 0] + @array.send(@method, (..0).step(-6)).should == [5] + @array.send(@method, (..0).step(-7)).should == [5] + + @array.send(@method, (...0).step(-1)).should == [5, 4, 3, 2, 1] + @array.send(@method, (...0).step(-2)).should == [5, 3, 1] + @array.send(@method, (...0).step(-3)).should == [5, 2] + @array.send(@method, (...0).step(-4)).should == [5, 1] + @array.send(@method, (...0).step(-5)).should == [5] + @array.send(@method, (...0).step(-6)).should == [5] + + @array.send(@method, (...1).step(-1)).should == [5, 4, 3, 2] + @array.send(@method, (...1).step(-2)).should == [5, 3] + @array.send(@method, (...1).step(-3)).should == [5, 2] + @array.send(@method, (...1).step(-4)).should == [5] + @array.send(@method, (...1).step(-5)).should == [5] + + @array.send(@method, (..-5).step(-1)).should == [5, 4, 3, 2, 1] + @array.send(@method, (..-5).step(-2)).should == [5, 3, 1] + @array.send(@method, (..-5).step(-3)).should == [5, 2] + @array.send(@method, (..-5).step(-4)).should == [5, 1] + @array.send(@method, (..-5).step(-5)).should == [5] + @array.send(@method, (..-5).step(-6)).should == [5] + + @array.send(@method, (...-5).step(-1)).should == [5, 4, 3, 2] + @array.send(@method, (...-5).step(-2)).should == [5, 3] + @array.send(@method, (...-5).step(-3)).should == [5, 2] + @array.send(@method, (...-5).step(-4)).should == [5] + @array.send(@method, (...-5).step(-5)).should == [5] + + @array.send(@method, (4..1).step(-1)).should == [4, 3, 2, 1] + @array.send(@method, (4..1).step(-2)).should == [4, 2] + @array.send(@method, (4..1).step(-3)).should == [4, 1] + @array.send(@method, (4..1).step(-4)).should == [4] + @array.send(@method, (4..1).step(-5)).should == [4] + + @array.send(@method, (4...1).step(-1)).should == [4, 3, 2] + @array.send(@method, (4...1).step(-2)).should == [4, 2] + @array.send(@method, (4...1).step(-3)).should == [4] + @array.send(@method, (4...1).step(-4)).should == [4] + + @array.send(@method, (-2..1).step(-1)).should == [4, 3, 2, 1] + @array.send(@method, (-2..1).step(-2)).should == [4, 2] + @array.send(@method, (-2..1).step(-3)).should == [4, 1] + @array.send(@method, (-2..1).step(-4)).should == [4] + @array.send(@method, (-2..1).step(-5)).should == [4] + + @array.send(@method, (-2...1).step(-1)).should == [4, 3, 2] + @array.send(@method, (-2...1).step(-2)).should == [4, 2] + @array.send(@method, (-2...1).step(-3)).should == [4] + @array.send(@method, (-2...1).step(-4)).should == [4] + + @array.send(@method, (4..-5).step(-1)).should == [4, 3, 2, 1] + @array.send(@method, (4..-5).step(-2)).should == [4, 2] + @array.send(@method, (4..-5).step(-3)).should == [4, 1] + @array.send(@method, (4..-5).step(-4)).should == [4] + @array.send(@method, (4..-5).step(-5)).should == [4] + + @array.send(@method, (4...-5).step(-1)).should == [4, 3, 2] + @array.send(@method, (4...-5).step(-2)).should == [4, 2] + @array.send(@method, (4...-5).step(-3)).should == [4] + @array.send(@method, (4...-5).step(-4)).should == [4] + + @array.send(@method, (-2..-5).step(-1)).should == [4, 3, 2, 1] + @array.send(@method, (-2..-5).step(-2)).should == [4, 2] + @array.send(@method, (-2..-5).step(-3)).should == [4, 1] + @array.send(@method, (-2..-5).step(-4)).should == [4] + @array.send(@method, (-2..-5).step(-5)).should == [4] + + @array.send(@method, (-2...-5).step(-1)).should == [4, 3, 2] + @array.send(@method, (-2...-5).step(-2)).should == [4, 2] + @array.send(@method, (-2...-5).step(-3)).should == [4] + @array.send(@method, (-2...-5).step(-4)).should == [4] + end end + end - it "can accept nil...nil ranges" do - a = [0, 1, 2, 3, 4, 5] - a.send(@method, eval("(nil...nil)")).should == a - a.send(@method, eval("(...nil)")).should == a - a.send(@method, eval("(nil..)")).should == a - end + it "can accept nil...nil ranges" do + a = [0, 1, 2, 3, 4, 5] + a.send(@method, eval("(nil...nil)")).should == a + a.send(@method, (...nil)).should == a + a.send(@method, eval("(nil..)")).should == a end end diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb index fc82e19e2a..4941e098f6 100644 --- a/spec/ruby/core/array/shared/unshift.rb +++ b/spec/ruby/core/array/shared/unshift.rb @@ -22,6 +22,11 @@ describe :array_unshift, shared: true do a.should == [3, 4] end + it "returns self" do + a = [1, 2, 3] + a.send(@method, "a").should.equal?(a) + end + it "quietly ignores unshifting nothing" do [].send(@method).should == [] end @@ -43,4 +48,17 @@ describe :array_unshift, shared: true do it "raises a FrozenError on a frozen array when the array would not be modified" do -> { ArraySpecs.frozen_array.send(@method) }.should raise_error(FrozenError) end + + # https://github.com/oracle/truffleruby/issues/2772 + it "doesn't rely on Array#[]= so it can be overridden" do + subclass = Class.new(Array) do + def []=(*) + raise "[]= is called" + end + end + + array = subclass.new + array.send(@method, 1) + array.should == [1] + end end diff --git a/spec/ruby/core/array/shift_spec.rb b/spec/ruby/core/array/shift_spec.rb index b998e45d28..6b4ef39f77 100644 --- a/spec/ruby/core/array/shift_spec.rb +++ b/spec/ruby/core/array/shift_spec.rb @@ -116,21 +116,5 @@ describe "Array#shift" do it "does not return subclass instances with Array subclass" do ArraySpecs::MyArray[1, 2, 3].shift(2).should be_an_instance_of(Array) end - - ruby_version_is ''...'2.7' do - it "returns an untainted array even if the array is tainted" do - ary = [1, 2].taint - ary.shift(2).tainted?.should be_false - ary.shift(0).tainted?.should be_false - end - - it "keeps taint status" do - a = [1, 2].taint - a.shift(2) - a.tainted?.should be_true - a.shift(2) - a.tainted?.should be_true - end - end end end diff --git a/spec/ruby/core/array/shuffle_spec.rb b/spec/ruby/core/array/shuffle_spec.rb index b255147c75..1d528c124f 100644 --- a/spec/ruby/core/array/shuffle_spec.rb +++ b/spec/ruby/core/array/shuffle_spec.rb @@ -47,6 +47,10 @@ describe "Array#shuffle" do [1, 2].shuffle(random: random).should be_an_instance_of(Array) end + it "accepts a Random class for the value for random: argument" do + [1, 2].shuffle(random: Random).should be_an_instance_of(Array) + end + it "calls #to_int on the Object returned by #rand" do value = mock("array_shuffle_random_value") value.should_receive(:to_int).at_least(1).times.and_return(0) @@ -93,4 +97,14 @@ describe "Array#shuffle!" do -> { ArraySpecs.frozen_array.shuffle! }.should raise_error(FrozenError) -> { ArraySpecs.empty_frozen_array.shuffle! }.should raise_error(FrozenError) end + + it "matches CRuby with random:" do + %w[a b c].shuffle(random: Random.new(1)).should == %w[a c b] + (0..10).to_a.shuffle(random: Random.new(10)).should == [2, 6, 8, 5, 7, 10, 3, 1, 0, 4, 9] + end + + it "matches CRuby with srand" do + srand(123) + %w[a b c d e f g h i j k].shuffle.should == %w[a e f h i j d b g k c] + end end diff --git a/spec/ruby/core/array/slice_spec.rb b/spec/ruby/core/array/slice_spec.rb index 8c276f9084..731c129251 100644 --- a/spec/ruby/core/array/slice_spec.rb +++ b/spec/ruby/core/array/slice_spec.rb @@ -172,16 +172,14 @@ describe "Array#slice!" do a.should == [1, 2] end - ruby_version_is "2.7" do - it "works with beginless ranges" do - a = [0,1,2,3,4] - a.slice!(eval("(..3)")).should == [0, 1, 2, 3] - a.should == [4] - - a = [0,1,2,3,4] - a.slice!(eval("(...-2)")).should == [0, 1, 2] - a.should == [3, 4] - end + it "works with beginless ranges" do + a = [0,1,2,3,4] + a.slice!((..3)).should == [0, 1, 2, 3] + a.should == [4] + + a = [0,1,2,3,4] + a.slice!((...-2)).should == [0, 1, 2] + a.should == [3, 4] end describe "with a subclass of Array" do @@ -189,56 +187,28 @@ describe "Array#slice!" do @array = ArraySpecs::MyArray[1, 2, 3, 4, 5] end - ruby_version_is ''...'3.0' do - it "returns a subclass instance with [n, m]" do - @array.slice!(0, 2).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [-n, m]" do - @array.slice!(-3, 2).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [n..m]" do - @array.slice!(1..3).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [n...m]" do - @array.slice!(1...3).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [-n..-m]" do - @array.slice!(-3..-1).should be_an_instance_of(ArraySpecs::MyArray) - end - - it "returns a subclass instance with [-n...-m]" do - @array.slice!(-3...-1).should be_an_instance_of(ArraySpecs::MyArray) - end + it "returns a Array instance with [n, m]" do + @array.slice!(0, 2).should be_an_instance_of(Array) end - ruby_version_is '3.0' do - it "returns a Array instance with [n, m]" do - @array.slice!(0, 2).should be_an_instance_of(Array) - end - - it "returns a Array instance with [-n, m]" do - @array.slice!(-3, 2).should be_an_instance_of(Array) - end + it "returns a Array instance with [-n, m]" do + @array.slice!(-3, 2).should be_an_instance_of(Array) + end - it "returns a Array instance with [n..m]" do - @array.slice!(1..3).should be_an_instance_of(Array) - end + it "returns a Array instance with [n..m]" do + @array.slice!(1..3).should be_an_instance_of(Array) + end - it "returns a Array instance with [n...m]" do - @array.slice!(1...3).should be_an_instance_of(Array) - end + it "returns a Array instance with [n...m]" do + @array.slice!(1...3).should be_an_instance_of(Array) + end - it "returns a Array instance with [-n..-m]" do - @array.slice!(-3..-1).should be_an_instance_of(Array) - end + it "returns a Array instance with [-n..-m]" do + @array.slice!(-3..-1).should be_an_instance_of(Array) + end - it "returns a Array instance with [-n...-m]" do - @array.slice!(-3...-1).should be_an_instance_of(Array) - end + it "returns a Array instance with [-n...-m]" do + @array.slice!(-3...-1).should be_an_instance_of(Array) end end end diff --git a/spec/ruby/core/array/sort_by_spec.rb b/spec/ruby/core/array/sort_by_spec.rb index 7cea6ec6d3..0334f953f6 100644 --- a/spec/ruby/core/array/sort_by_spec.rb +++ b/spec/ruby/core/array/sort_by_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/iterable_and_tolerating_size_increasing' require_relative '../enumerable/shared/enumeratorized' describe "Array#sort_by!" do @@ -31,6 +32,11 @@ describe "Array#sort_by!" do -> { ArraySpecs.empty_frozen_array.sort_by! {}}.should raise_error(FrozenError) 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 [1, 2, 3].sort_by!{ break :a }.should == :a end @@ -48,5 +54,32 @@ describe "Array#sort_by!" do [1].sort_by!(&:to_s).should == [1] 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] end + +describe "Array#sort_by!" do + it_behaves_like :array_iterable_and_tolerating_size_increasing, :sort_by! +end diff --git a/spec/ruby/core/array/sum_spec.rb b/spec/ruby/core/array/sum_spec.rb index 8ca8353a67..06abe06135 100644 --- a/spec/ruby/core/array/sum_spec.rb +++ b/spec/ruby/core/array/sum_spec.rb @@ -1,4 +1,5 @@ require_relative '../../spec_helper' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#sum" do it "returns the sum of elements" do @@ -9,8 +10,12 @@ describe "Array#sum" do [1, 2, 3].sum { |i| i * 10 }.should == 60 end + it "doesn't apply the block init" do + [1, 2, 3].sum(1) { |i| i * 10 }.should == 61 + end + # https://bugs.ruby-lang.org/issues/12217 - # https://github.com/ruby/ruby/blob/master/doc/ChangeLog-2.4.0#L6208-L6214 + # https://github.com/ruby/ruby/blob/master/doc/ChangeLog/ChangeLog-2.4.0#L6208-L6214 it "uses Kahan's compensated summation algorithm for precise sum of float numbers" do floats = [2.7800000000000002, 5.0, 2.5, 4.44, 3.89, 3.89, 4.44, 7.78, 5.0, 2.7800000000000002, 5.0, 2.5] naive_sum = floats.reduce { |sum, e| sum + e } @@ -68,4 +73,18 @@ describe "Array#sum" do a.should_receive(:+).with(b).and_return(42) [b].sum(a).should == 42 end + + ruby_bug '#19530', ''...'3.3' do + it "calls + on the init value" do + a = mock("a") + b = mock("b") + a.should_receive(:+).with(42).and_return(b) + [42].sum(a).should == b + end + end +end + +describe "Array#sum" do + @value_to_return = -> _ { 1 } + it_behaves_like :array_iterable_and_tolerating_size_increasing, :sum end diff --git a/spec/ruby/core/array/take_spec.rb b/spec/ruby/core/array/take_spec.rb index 4fb6f0ce75..c4f0ac9aa4 100644 --- a/spec/ruby/core/array/take_spec.rb +++ b/spec/ruby/core/array/take_spec.rb @@ -26,15 +26,7 @@ describe "Array#take" do ->{ [1].take(-3) }.should raise_error(ArgumentError) end - ruby_version_is ''...'3.0' do - it 'returns a subclass instance for Array subclasses' do - ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should be_an_instance_of(ArraySpecs::MyArray) - end - end - - ruby_version_is '3.0' do - it 'returns a Array instance for Array subclasses' do - ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should be_an_instance_of(Array) - end + it 'returns a Array instance for Array subclasses' do + ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should be_an_instance_of(Array) end end diff --git a/spec/ruby/core/array/take_while_spec.rb b/spec/ruby/core/array/take_while_spec.rb index 363419b265..8f50260b42 100644 --- a/spec/ruby/core/array/take_while_spec.rb +++ b/spec/ruby/core/array/take_while_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#take_while" do it "returns all elements until the block returns false" do @@ -14,15 +15,12 @@ describe "Array#take_while" do [1, 2, false, 4].take_while{ |element| element }.should == [1, 2] end - ruby_version_is ''...'3.0' do - it 'returns a subclass instance for Array subclasses' do - ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should be_an_instance_of(ArraySpecs::MyArray) - end + it 'returns a Array instance for Array subclasses' do + ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should be_an_instance_of(Array) end +end - ruby_version_is '3.0' do - it 'returns a Array instance for Array subclasses' do - ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should be_an_instance_of(Array) - end - end +describe "Array#take_while" do + @value_to_return = -> _ { true } + it_behaves_like :array_iterable_and_tolerating_size_increasing, :take_while end diff --git a/spec/ruby/core/array/to_h_spec.rb b/spec/ruby/core/array/to_h_spec.rb index f5a7e546e6..f4578211a1 100644 --- a/spec/ruby/core/array/to_h_spec.rb +++ b/spec/ruby/core/array/to_h_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#to_h" do it "converts empty array to empty hash" do @@ -77,3 +78,8 @@ describe "Array#to_h" do 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 diff --git a/spec/ruby/core/array/try_convert_spec.rb b/spec/ruby/core/array/try_convert_spec.rb index 47b4722d80..bea8815006 100644 --- a/spec/ruby/core/array/try_convert_spec.rb +++ b/spec/ruby/core/array/try_convert_spec.rb @@ -39,7 +39,7 @@ describe "Array.try_convert" do it "sends #to_ary to the argument and raises TypeError if it's not a kind of Array" do obj = mock("to_ary") obj.should_receive(:to_ary).and_return(Object.new) - -> { Array.try_convert obj }.should raise_error(TypeError) + -> { Array.try_convert obj }.should raise_error(TypeError, "can't convert MockObject to Array (MockObject#to_ary gives Object)") end it "does not rescue exceptions raised by #to_ary" do diff --git a/spec/ruby/core/array/uniq_spec.rb b/spec/ruby/core/array/uniq_spec.rb index 5911c23e6a..d5d826db15 100644 --- a/spec/ruby/core/array/uniq_spec.rb +++ b/spec/ruby/core/array/uniq_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#uniq" do it "returns an array with no duplicates" do @@ -39,76 +40,32 @@ describe "Array#uniq" do [x, y].uniq.should == [x, y] end - ruby_version_is '2.7' do - it "compares elements with matching hash codes with #eql?" do - a = Array.new(2) do - obj = mock('0') - obj.should_receive(:hash).at_least(1).and_return(0) + it "compares elements with matching hash codes with #eql?" do + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) - def obj.eql?(o) - false - end - - obj + def obj.eql?(o) + false end - a.uniq.should == a - - a = Array.new(2) do - obj = mock('0') - obj.should_receive(:hash).at_least(1).and_return(0) - - def obj.eql?(o) - true - end - - obj - end - - a.uniq.size.should == 1 + obj end - end - ruby_version_is ''...'2.7' do - it "compares elements with matching hash codes with #eql?" do - a = Array.new(2) do - obj = mock('0') - obj.should_receive(:hash).at_least(1).and_return(0) - - def obj.eql?(o) - # It's undefined whether the impl does a[0].eql?(a[1]) or - # a[1].eql?(a[0]) so we taint both. - taint - o.taint - false - end - - obj - end + a.uniq.should == a - a.uniq.should == a - a[0].should.tainted? - a[1].should.tainted? + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) - a = Array.new(2) do - obj = mock('0') - obj.should_receive(:hash).at_least(1).and_return(0) - - def obj.eql?(o) - # It's undefined whether the impl does a[0].eql?(a[1]) or - # a[1].eql?(a[0]) so we taint both. - taint - o.taint - true - end - - obj + def obj.eql?(o) + true end - a.uniq.size.should == 1 - a[0].should.tainted? - a[1].should.tainted? + obj end + + a.uniq.size.should == 1 end it "compares elements based on the value returned from the block" do @@ -128,16 +85,8 @@ describe "Array#uniq" do [false, nil, 42].uniq { :bar }.should == [false] end - ruby_version_is ''...'3.0' do - it "returns subclass instance on Array subclasses" do - ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(ArraySpecs::MyArray) - end - end - - ruby_version_is '3.0' do - it "returns Array instance on Array subclasses" do - ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(Array) - end + it "returns Array instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(Array) end it "properly handles an identical item even when its #eql? isn't reflexive" do @@ -175,6 +124,11 @@ describe "Array#uniq" do 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 it "modifies the array in place" do a = [ "a", "a", "b", "b", "c" ] @@ -258,4 +212,32 @@ describe "Array#uniq!" do a.uniq! a.should == [x] 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 diff --git a/spec/ruby/core/array/values_at_spec.rb b/spec/ruby/core/array/values_at_spec.rb index f1522e0bfe..e85bbee400 100644 --- a/spec/ruby/core/array/values_at_spec.rb +++ b/spec/ruby/core/array/values_at_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +# Should be synchronized with core/struct/values_at_spec.rb describe "Array#values_at" do it "returns an array of elements at the indexes when passed indexes" do [1, 2, 3, 4, 5].values_at().should == [] @@ -66,10 +67,8 @@ describe "Array#values_at" do [1, 2, 3, 4].values_at(eval("(3...)")).should == [4] end - ruby_version_is "2.7" do - it "works when given beginless ranges" do - [1, 2, 3, 4].values_at(eval("(..2)")).should == [1, 2, 3] - [1, 2, 3, 4].values_at(eval("(...2)")).should == [1, 2] - end + it "works when given beginless ranges" do + [1, 2, 3, 4].values_at((..2)).should == [1, 2, 3] + [1, 2, 3, 4].values_at((...2)).should == [1, 2] end end diff --git a/spec/ruby/core/array/zip_spec.rb b/spec/ruby/core/array/zip_spec.rb index af4013debe..2a0f64cb49 100644 --- a/spec/ruby/core/array/zip_spec.rb +++ b/spec/ruby/core/array/zip_spec.rb @@ -62,4 +62,10 @@ describe "Array#zip" do it "does not return subclass instance on Array subclasses" do ArraySpecs::MyArray[1, 2, 3].zip(["a", "b"]).should be_an_instance_of(Array) end + + it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do + -> { [1, 2, 3].zip(Object.new) }.should raise_error(TypeError, "wrong argument type Object (must respond to :each)") + -> { [1, 2, 3].zip(1) }.should raise_error(TypeError, "wrong argument type Integer (must respond to :each)") + -> { [1, 2, 3].zip(true) }.should raise_error(TypeError, "wrong argument type TrueClass (must respond to :each)") + end end |