diff options
Diffstat (limited to 'spec/ruby/core/array/shared')
| -rw-r--r-- | spec/ruby/core/array/shared/clone.rb | 26 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/collect.rb | 83 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/delete_if.rb | 24 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/difference.rb | 78 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/index.rb | 4 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/inspect.rb | 51 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/intersection.rb | 85 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb | 25 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/join.rb | 103 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/keep_if.rb | 49 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/push.rb | 6 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/replace.rb | 6 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/select.rb | 35 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/slice.rb | 442 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/union.rb | 79 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/unshift.rb | 26 |
16 files changed, 881 insertions, 241 deletions
diff --git a/spec/ruby/core/array/shared/clone.rb b/spec/ruby/core/array/shared/clone.rb index 6fc7ae31eb..035b45ec99 100644 --- a/spec/ruby/core/array/shared/clone.rb +++ b/spec/ruby/core/array/shared/clone.rb @@ -7,8 +7,8 @@ describe :array_clone, shared: true do it "produces a shallow copy where the references are directly copied" do a = [mock('1'), mock('2')] b = a.send @method - b.first.object_id.should == a.first.object_id - b.last.object_id.should == a.last.object_id + b.first.should equal a.first + b.last.should equal a.last end it "creates a new array containing all elements or the original" do @@ -17,26 +17,4 @@ describe :array_clone, shared: true do b.should == a b.__id__.should_not == a.__id__ end - - 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.tainted?.should == true - bb.tainted?.should == false - 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.untrusted?.should == true - bb.untrusted?.should == false - end end diff --git a/spec/ruby/core/array/shared/collect.rb b/spec/ruby/core/array/shared/collect.rb index f6bcfd8904..030302ced6 100644 --- a/spec/ruby/core/array/shared/collect.rb +++ b/spec/ruby/core/array/shared/collect.rb @@ -1,11 +1,12 @@ -require File.expand_path('../../../enumerable/shared/enumeratorized', __FILE__) +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 a = ['a', 'b', 'c', 'd'] b = a.send(@method) { |i| i + '!' } b.should == ["a!", "b!", "c!", "d!"] - b.object_id.should_not == a.object_id + b.should_not equal a end it "does not return subclass instance" do @@ -37,27 +38,17 @@ describe :array_collect, shared: true do it "raises an ArgumentError when no block and with arguments" do a = [1, 2, 3] - lambda { + -> { a.send(@method, :foo) }.should raise_error(ArgumentError) end - 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 - 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 @@ -70,7 +61,7 @@ describe :array_collect_b, shared: true do it "returns self" do a = [1, 2, 3, 4, 5] b = a.send(@method) {|i| i+1 } - a.object_id.should == b.object_id + a.should equal b end it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do @@ -94,43 +85,57 @@ describe :array_collect_b, shared: true do a.should == ["1!", "2!", "3!"] end - 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 - describe "when frozen" do - it "raises a RuntimeError" do - lambda { ArraySpecs.frozen_array.send(@method) {} }.should raise_error(RuntimeError) + it "raises a FrozenError" do + -> { ArraySpecs.frozen_array.send(@method) {} }.should raise_error(FrozenError) end - it "raises a RuntimeError when empty" do - lambda { ArraySpecs.empty_frozen_array.send(@method) {} }.should raise_error(RuntimeError) + it "raises a FrozenError when empty" do + -> { ArraySpecs.empty_frozen_array.send(@method) {} }.should raise_error(FrozenError) end - it "raises a RuntimeError when calling #each on the returned Enumerator" do + it "raises a FrozenError when calling #each on the returned Enumerator" do enumerator = ArraySpecs.frozen_array.send(@method) - lambda { enumerator.each {|x| x } }.should raise_error(RuntimeError) + -> { enumerator.each {|x| x } }.should raise_error(FrozenError) end - it "raises a RuntimeError when calling #each on the returned Enumerator when empty" do + it "raises a FrozenError when calling #each on the returned Enumerator when empty" do enumerator = ArraySpecs.empty_frozen_array.send(@method) - lambda { enumerator.each {|x| x } }.should raise_error(RuntimeError) + -> { enumerator.each {|x| x } }.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 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/delete_if.rb b/spec/ruby/core/array/shared/delete_if.rb index a9fb57e0d9..a3fdcf4fac 100644 --- a/spec/ruby/core/array/shared/delete_if.rb +++ b/spec/ruby/core/array/shared/delete_if.rb @@ -3,25 +3,11 @@ describe :delete_if, shared: true do @object = [1,2,3] end - ruby_version_is "2.3" do - it "updates the receiver after all blocks" do - @object.send(@method) do |e| - @object.length.should == 3 - true - end - @object.length.should == 0 - end - end - - ruby_version_is ""..."2.3" do - it "updates the receiver after each true block" do - count = 0 - @object.send(@method) do |e| - @object.length.should == (3 - count) - count += 1 - true - end - @object.length.should == 0 + it "updates the receiver after all blocks" do + @object.send(@method) do |e| + @object.length.should == 3 + true end + @object.length.should == 0 end end diff --git a/spec/ruby/core/array/shared/difference.rb b/spec/ruby/core/array/shared/difference.rb new file mode 100644 index 0000000000..3e69050d82 --- /dev/null +++ b/spec/ruby/core/array/shared/difference.rb @@ -0,0 +1,78 @@ +describe :array_binary_difference, shared: true do + it "creates an array minus any items from other array" do + [].send(@method, [ 1, 2, 4 ]).should == [] + [1, 2, 4].send(@method, []).should == [1, 2, 4] + [ 1, 2, 3, 4, 5 ].send(@method, [ 1, 2, 4 ]).should == [3, 5] + end + + it "removes multiple items on the lhs equal to one on the rhs" do + [1, 1, 2, 2, 3, 3, 4, 5].send(@method, [1, 2, 4]).should == [3, 3, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.send(@method, empty).should == [] + + [].send(@method, ArraySpecs.recursive_array).should == [] + + array = ArraySpecs.recursive_array + array.send(@method, array).should == [] + end + + it "tries to convert the passed arguments to Arrays using #to_ary" do + obj = mock('[2,3,3,4]') + obj.should_receive(:to_ary).and_return([2, 3, 3, 4]) + [1, 1, 2, 2, 3, 4].send(@method, obj).should == [1, 1] + end + + it "raises a TypeError if the argument cannot be coerced to an Array by calling #to_ary" do + obj = mock('not an array') + -> { [1, 2, 3].send(@method, obj) }.should raise_error(TypeError) + end + + it "does not return subclass instance for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[]).should be_an_instance_of(Array) + [1, 2, 3].send(@method, ArraySpecs::MyArray[]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + [5, 6, 7].send(@method, ArraySpecs::ToAryArray[7]).should == [5, 6] + end + + it "removes an item identified as equivalent via #hash and #eql?" do + obj1 = mock('1') + obj2 = mock('2') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(true) + + [obj1].send(@method, [obj2]).should == [] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [] + end + + it "doesn't remove an item with the same hash but not #eql?" do + obj1 = mock('1') + obj2 = mock('2') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(false) + + [obj1].send(@method, [obj2]).should == [obj1] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1, obj1] + end + + it "removes an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.stub!(:hash).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + [x].send(@method, [x]).should == [] + end + + it "is not destructive" do + a = [1, 2, 3] + a.send(@method, [1]) + a.should == [1, 2, 3] + end +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 6a60781b45..af5128c645 100644 --- a/spec/ruby/core/array/shared/inspect.rb +++ b/spec/ruby/core/array/shared/inspect.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../fixtures/encoded_strings', __FILE__) +require_relative '../fixtures/encoded_strings' describe :array_inspect, shared: true do it "returns a string" do @@ -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"]' @@ -55,7 +55,7 @@ describe :array_inspect, shared: true do obj.should_receive(:inspect).and_return(obj) obj.should_receive(:to_s).and_raise(Exception) - lambda { [obj].send(@method) }.should raise_error(Exception) + -> { [obj].send(@method) }.should raise_error(Exception) end it "represents a recursive element with '[...]'" do @@ -64,30 +64,6 @@ describe :array_inspect, shared: true do ArraySpecs.empty_recursive_array.send(@method).should == "[[...]]" end - 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 - describe "with encoding" do before :each do @default_external_encoding = Encoding.default_external @@ -121,24 +97,11 @@ describe :array_inspect, shared: true do array.send(@method).encoding.name.should == "US-ASCII" end - ruby_version_is ''...'2.3' do - it "raises 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)) - - lambda { - [utf_16be].send(@method) - }.should raise_error(Encoding::CompatibilityError) - end - end - - ruby_version_is '2.3' do - 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)) + 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].send(@method).should == '["utf_16be \u3042"]' - end + [utf_16be].send(@method).should == '["utf_16be \u3042"]' end end end diff --git a/spec/ruby/core/array/shared/intersection.rb b/spec/ruby/core/array/shared/intersection.rb new file mode 100644 index 0000000000..0b4166ab63 --- /dev/null +++ b/spec/ruby/core/array/shared/intersection.rb @@ -0,0 +1,85 @@ +describe :array_intersection, shared: true do + it "creates an array with elements common to both arrays (intersection)" do + [].send(@method, []).should == [] + [1, 2].send(@method, []).should == [] + [].send(@method, [1, 2]).should == [] + [ 1, 3, 5 ].send(@method, [ 1, 2, 3 ]).should == [1, 3] + end + + it "creates an array with no duplicates" do + [ 1, 1, 3, 5 ].send(@method, [ 1, 2, 3 ]).uniq!.should == nil + end + + it "creates an array with elements in order they are first encountered" do + [ 1, 2, 3, 2, 5, 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 + a = [1, 1, 3, 5] + a.send(@method, [1, 2, 3]).should == [1, 3] + a.should == [1, 1, 3, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.send(@method, empty).should == empty + + ArraySpecs.recursive_array.send(@method, []).should == [] + [].send(@method, ArraySpecs.recursive_array).should == [] + + ArraySpecs.recursive_array.send(@method, ArraySpecs.recursive_array).should == [1, 'two', 3.0, ArraySpecs.recursive_array] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('[1,2,3]') + obj.should_receive(:to_ary).and_return([1, 2, 3]) + [1, 2].send(@method, obj).should == ([1, 2]) + end + + it "determines equivalence between elements in the sense of eql?" do + not_supported_on :opal do + [5.0, 4.0].send(@method, [5, 4]).should == [] + end + + str = "x" + [str].send(@method, [str.dup]).should == [str] + + obj1 = mock('1') + obj2 = mock('2') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(true) + obj2.stub!(:eql?).and_return(true) + + [obj1].send(@method, [obj2]).should == [obj1] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1] + + obj1 = mock('3') + obj2 = mock('4') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(false) + + [obj1].send(@method, [obj2]).should == [] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj2] + end + + it "does return subclass instances for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + [5, 6].send(@method, ArraySpecs::ToAryArray[1, 2, 5, 6]).should == [5, 6] + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.stub!(:hash).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + [x].send(@method, [x]).should == [x] + end +end 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 fa66588b47..507b13e3c8 100644 --- a/spec/ruby/core/array/shared/join.rb +++ b/spec/ruby/core/array/shared/join.rb @@ -1,5 +1,5 @@ -require File.expand_path('../../fixtures/classes', __FILE__) -require File.expand_path('../../fixtures/encoded_strings', __FILE__) +require_relative '../fixtures/classes' +require_relative '../fixtures/encoded_strings' describe :array_join_with_default_separator, shared: true do before :each do @@ -19,8 +19,10 @@ describe :array_join_with_default_separator, shared: true do end it "returns a string formed by concatenating each String element separated by $," do - $, = " | " - ["1", "2", "3"].send(@method).should == "1 | 2 | 3" + suppress_warning { + $, = " | " + ["1", "2", "3"].send(@method).should == "1 | 2 | 3" + } end it "attempts coercion via #to_str first" do @@ -47,48 +49,20 @@ describe :array_join_with_default_separator, shared: true do it "raises a NoMethodError if an element does not respond to #to_str, #to_ary, or #to_s" do obj = mock('o') class << obj; undef :to_s; end - lambda { [1, obj].send(@method) }.should raise_error(NoMethodError) + -> { [1, obj].send(@method) }.should raise_error(NoMethodError) end it "raises an ArgumentError when the Array is recursive" do - lambda { ArraySpecs.recursive_array.send(@method) }.should raise_error(ArgumentError) - lambda { ArraySpecs.head_recursive_array.send(@method) }.should raise_error(ArgumentError) - lambda { ArraySpecs.empty_recursive_array.send(@method) }.should raise_error(ArgumentError) - end - - 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 + -> { ArraySpecs.recursive_array.send(@method) }.should raise_error(ArgumentError) + -> { ArraySpecs.head_recursive_array.send(@method) }.should raise_error(ArgumentError) + -> { ArraySpecs.empty_recursive_array.send(@method) }.should raise_error(ArgumentError) 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 - ary3 = ArraySpecs.array_with_utf8_and_7bit_ascii8bit_strings - ary4 = ArraySpecs.array_with_usascii_and_7bit_ascii8bit_strings + ary3 = ArraySpecs.array_with_utf8_and_7bit_binary_strings + ary4 = ArraySpecs.array_with_usascii_and_7bit_binary_strings ary1.send(@method).encoding.should == Encoding::UTF_8 ary2.send(@method).encoding.should == Encoding::US_ASCII @@ -105,9 +79,22 @@ describe :array_join_with_default_separator, shared: true do end it "fails for arrays with incompatibly-encoded strings" do - ary_utf8_bad_ascii8bit = ArraySpecs.array_with_utf8_and_ascii8bit_strings + ary_utf8_bad_binary = ArraySpecs.array_with_utf8_and_binary_strings - lambda { ary_utf8_bad_ascii8bit.send(@method) }.should raise_error(EncodingError) + -> { ary_utf8_bad_binary.send(@method) }.should raise_error(EncodingError) + 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 end end @@ -122,40 +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 - - 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 diff --git a/spec/ruby/core/array/shared/keep_if.rb b/spec/ruby/core/array/shared/keep_if.rb index 581ba31d1b..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 File.expand_path('../../../enumerable/shared/enumeratorized', __FILE__) +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 @@ -37,24 +38,58 @@ describe :keep_if, shared: true do describe "with truthy block" do it "keeps elements after any exception" do - lambda { @frozen.send(@method) { true } }.should raise_error(Exception) + -> { @frozen.send(@method) { true } }.should raise_error(Exception) @frozen.should == @origin end - it "raises a RuntimeError" do - lambda { @frozen.send(@method) { true } }.should raise_error(RuntimeError) + it "raises a FrozenError" do + -> { @frozen.send(@method) { true } }.should raise_error(FrozenError) end end describe "with falsy block" do it "keeps elements after any exception" do - lambda { @frozen.send(@method) { false } }.should raise_error(Exception) + -> { @frozen.send(@method) { false } }.should raise_error(Exception) @frozen.should == @origin end - it "raises a RuntimeError" do - lambda { @frozen.send(@method) { false } }.should raise_error(RuntimeError) + it "raises a FrozenError" 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/push.rb b/spec/ruby/core/array/shared/push.rb index 5951b71a19..ac790fb6a4 100644 --- a/spec/ruby/core/array/shared/push.rb +++ b/spec/ruby/core/array/shared/push.rb @@ -26,8 +26,8 @@ describe :array_push, shared: true do array.send(@method, :last).should == [1, 'two', 3.0, array, array, array, array, array, :last] end - it "raises a RuntimeError on a frozen array" do - lambda { ArraySpecs.frozen_array.send(@method, 1) }.should raise_error(RuntimeError) - lambda { ArraySpecs.frozen_array.send(@method) }.should raise_error(RuntimeError) + it "raises a FrozenError on a frozen array" do + -> { ArraySpecs.frozen_array.send(@method, 1) }.should raise_error(FrozenError) + -> { ArraySpecs.frozen_array.send(@method) }.should raise_error(FrozenError) end end diff --git a/spec/ruby/core/array/shared/replace.rb b/spec/ruby/core/array/shared/replace.rb index 8442d9a841..9a6e60c1b0 100644 --- a/spec/ruby/core/array/shared/replace.rb +++ b/spec/ruby/core/array/shared/replace.rb @@ -52,9 +52,9 @@ describe :array_replace, shared: true do [].send(@method, ArraySpecs::ToAryArray[5, 6, 7]).should == [5, 6, 7] end - it "raises a RuntimeError on a frozen array" do - lambda { + it "raises a FrozenError on a frozen array" do + -> { ArraySpecs.frozen_array.send(@method, ArraySpecs.frozen_array) - }.should raise_error(RuntimeError) + }.should raise_error(FrozenError) end end diff --git a/spec/ruby/core/array/shared/select.rb b/spec/ruby/core/array/shared/select.rb new file mode 100644 index 0000000000..9c2cbf76c4 --- /dev/null +++ b/spec/ruby/core/array/shared/select.rb @@ -0,0 +1,35 @@ +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 + it_should_behave_like :enumeratorized_with_origin_size + + it "returns a new array of elements for which block is true" do + [1, 3, 4, 5, 6, 9].send(@method) { |i| i % ((i + 1) / 2) == 0}.should == [1, 4, 6] + end + + it "does not return subclass instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].send(@method) { true }.should be_an_instance_of(Array) + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.send(@method) { true }.should == empty + empty.send(@method) { false }.should == [] + + array = ArraySpecs.recursive_array + array.send(@method) { true }.should == [1, 'two', 3.0, array, array, array, array, array] + array.send(@method) { false }.should == [] + end +end diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb index b3f4ccb9a6..b80261d32f 100644 --- a/spec/ruby/core/array/shared/slice.rb +++ b/spec/ruby/core/array/shared/slice.rb @@ -117,6 +117,27 @@ describe :array_slice, shared: true do a.send(@method, 0, obj).should == [1, 2] end + it "raises TypeError if to_int returns non-integer" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + a = [1, 2, 3, 4, 5] + + def from.to_int() 'cat' end + def to.to_int() -2 end + + -> { a.send(@method, from..to) }.should raise_error(TypeError) + + def from.to_int() 1 end + def to.to_int() 'cat' end + + -> { a.send(@method, from..to) }.should raise_error(TypeError) + end + it "returns the elements specified by Range indexes with [m..n]" do [ "a", "b", "c", "d", "e" ].send(@method, 1..3).should == ["b", "c", "d"] [ "a", "b", "c", "d", "e" ].send(@method, 4..-1).should == ['e'] @@ -266,10 +287,10 @@ describe :array_slice, shared: true do a.send(@method, 1..0).should == [] a.send(@method, 1...0).should == [] - lambda { a.send(@method, "a" .. "b") }.should raise_error(TypeError) - lambda { a.send(@method, "a" ... "b") }.should raise_error(TypeError) - lambda { a.send(@method, from .. "b") }.should raise_error(TypeError) - lambda { a.send(@method, from ... "b") }.should raise_error(TypeError) + -> { a.send(@method, "a" .. "b") }.should raise_error(TypeError) + -> { a.send(@method, "a" ... "b") }.should raise_error(TypeError) + -> { a.send(@method, from .. "b") }.should raise_error(TypeError) + -> { a.send(@method, from ... "b") }.should raise_error(TypeError) end it "returns the same elements as [m..n] and [m...n] with Range subclasses" do @@ -376,28 +397,28 @@ describe :array_slice, shared: true do @array = ArraySpecs::MyArray[1, 2, 3, 4, 5] end - it "returns a subclass instance with [n, m]" do - @array.send(@method, 0, 2).should be_an_instance_of(ArraySpecs::MyArray) + it "returns a Array instance with [n, m]" do + @array.send(@method, 0, 2).should be_an_instance_of(Array) end - it "returns a subclass instance with [-n, m]" do - @array.send(@method, -3, 2).should be_an_instance_of(ArraySpecs::MyArray) + it "returns a Array instance with [-n, m]" do + @array.send(@method, -3, 2).should be_an_instance_of(Array) end - it "returns a subclass instance with [n..m]" do - @array.send(@method, 1..3).should be_an_instance_of(ArraySpecs::MyArray) + it "returns a Array instance with [n..m]" do + @array.send(@method, 1..3).should be_an_instance_of(Array) end - it "returns a subclass instance with [n...m]" do - @array.send(@method, 1...3).should be_an_instance_of(ArraySpecs::MyArray) + it "returns a Array instance with [n...m]" do + @array.send(@method, 1...3).should be_an_instance_of(Array) end - it "returns a subclass instance with [-n..-m]" do - @array.send(@method, -3..-1).should be_an_instance_of(ArraySpecs::MyArray) + it "returns a Array instance with [-n..-m]" do + @array.send(@method, -3..-1).should be_an_instance_of(Array) end - it "returns a subclass instance with [-n...-m]" do - @array.send(@method, -3...-1).should be_an_instance_of(ArraySpecs::MyArray) + 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 @@ -440,20 +461,397 @@ describe :array_slice, shared: true do it "raises a RangeError when the start index is out of range of Fixnum" do array = [1, 2, 3, 4, 5, 6] obj = mock('large value') - obj.should_receive(:to_int).and_return(0x8000_0000_0000_0000_0000) - lambda { array.send(@method, obj) }.should raise_error(RangeError) + obj.should_receive(:to_int).and_return(bignum_value) + -> { array.send(@method, obj) }.should raise_error(RangeError) obj = 8e19 - lambda { array.send(@method, obj) }.should raise_error(RangeError) + -> { array.send(@method, obj) }.should raise_error(RangeError) + + # boundary value when longs are 64 bits + -> { array.send(@method, 2.0**63) }.should raise_error(RangeError) + + # just under the boundary value when longs are 64 bits + array.send(@method, max_long.to_f.prev_float).should == nil end it "raises a RangeError when the length is out of range of Fixnum" do array = [1, 2, 3, 4, 5, 6] obj = mock('large value') - obj.should_receive(:to_int).and_return(0x8000_0000_0000_0000_0000) - lambda { array.send(@method, 1, obj) }.should raise_error(RangeError) + obj.should_receive(:to_int).and_return(bignum_value) + -> { array.send(@method, 1, obj) }.should raise_error(RangeError) obj = 8e19 - lambda { array.send(@method, 1, obj) }.should raise_error(RangeError) + -> { array.send(@method, 1, obj) }.should raise_error(RangeError) + end + + it "raises a type error if a range is passed with a length" do + ->{ [1, 2, 3].send(@method, 1..2, 1) }.should raise_error(TypeError) + end + + it "raises a RangeError if passed a range with a bound that is too large" do + array = [1, 2, 3, 4, 5, 6] + -> { array.send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError) + -> { array.send(@method, 0..bignum_value) }.should raise_error(RangeError) + end + + it "can accept endless ranges" do + a = [0, 1, 2, 3, 4, 5] + a.send(@method, eval("(2..)")).should == [2, 3, 4, 5] + a.send(@method, eval("(2...)")).should == [2, 3, 4, 5] + a.send(@method, eval("(-2..)")).should == [4, 5] + a.send(@method, eval("(-2...)")).should == [4, 5] + a.send(@method, eval("(9..)")).should == nil + a.send(@method, eval("(9...)")).should == nil + a.send(@method, eval("(-9..)")).should == nil + a.send(@method, eval("(-9...)")).should == nil + 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] + + @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 + + 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, (..0).step(2)).should == [0] + @array.send(@method, (...0).step(2)).should == [] + + @array.send(@method, (..0).step(10)).should == [0] + @array.send(@method, (...0).step(10)).should == [] + + # 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, (..3).step(2)).should == [0, 2] + @array.send(@method, (...3).step(2)).should == [0, 2] + + @array.send(@method, (..3).step(10)).should == [0] + @array.send(@method, (...3).step(10)).should == [0] + + # 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, (..-2).step(2)).should == [0, 2, 4] + @array.send(@method, (...-2).step(2)).should == [0, 2] + + @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] + + @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 + + 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(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] + + @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] + + # 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(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] + + @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] + + # 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(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 == [] + + @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 == [] + + # 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(-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 == [] + + @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 == [] + + # 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(-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 == [] + + @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 + + 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(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 == [] + + @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 == [] + + # 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(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 == [] + + @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 + + 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) + + # 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 + + @array.send(@method, eval("(6..).step(2)")).should == [] + -> { @array.send(@method, eval("(7..).step(2)")) }.should raise_error(RangeError) + end + end + + 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 + + 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 + + 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/union.rb b/spec/ruby/core/array/shared/union.rb new file mode 100644 index 0000000000..0b60df9ca4 --- /dev/null +++ b/spec/ruby/core/array/shared/union.rb @@ -0,0 +1,79 @@ +describe :array_binary_union, shared: true do + it "returns an array of elements that appear in either array (union)" do + [].send(@method, []).should == [] + [1, 2].send(@method, []).should == [1, 2] + [].send(@method, [1, 2]).should == [1, 2] + [ 1, 2, 3, 4 ].send(@method, [ 3, 4, 5 ]).should == [1, 2, 3, 4, 5] + end + + it "creates an array with no duplicates" do + [ 1, 2, 3, 1, 4, 5 ].send(@method, [ 1, 3, 4, 5, 3, 6 ]).should == [1, 2, 3, 4, 5, 6] + end + + it "creates an array with elements in order they are first encountered" do + [ 1, 2, 3, 1 ].send(@method, [ 1, 3, 4, 5 ]).should == [1, 2, 3, 4, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.send(@method, empty).should == empty + + array = ArraySpecs.recursive_array + array.send(@method, []).should == [1, 'two', 3.0, array] + [].send(@method, array).should == [1, 'two', 3.0, array] + array.send(@method, array).should == [1, 'two', 3.0, array] + array.send(@method, empty).should == [1, 'two', 3.0, array, empty] + 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]) + [0].send(@method, obj).should == ([0] | [1, 2, 3]) + end + + # MRI follows hashing semantics here, so doesn't actually call eql?/hash for Integer/Symbol + it "acts as if using an intermediate hash to collect values" do + not_supported_on :opal do + [5.0, 4.0].send(@method, [5, 4]).should == [5.0, 4.0, 5, 4] + end + + str = "x" + [str].send(@method, [str.dup]).should == [str] + + obj1 = mock('1') + obj2 = mock('2') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj2.should_receive(:eql?).at_least(1).and_return(true) + + [obj1].send(@method, [obj2]).should == [obj1] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1] + + obj1 = mock('3') + obj2 = mock('4') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj2.should_receive(:eql?).at_least(1).and_return(false) + + [obj1].send(@method, [obj2]).should == [obj1, obj2] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1, obj2] + end + + it "does not return subclass instances for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + [1, 2].send(@method, ArraySpecs::ToAryArray[5, 6]).should == [1, 2, 5, 6] + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.stub!(:hash).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + [x].send(@method, [x]).should == [x] + end +end diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb index 367bab4166..9e0fe7556a 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 @@ -35,12 +40,25 @@ describe :array_unshift, shared: true do array[0..5].should == [:new, 1, 'two', 3.0, array, array] end - it "raises a RuntimeError on a frozen array when the array is modified" do - lambda { ArraySpecs.frozen_array.send(@method, 1) }.should raise_error(RuntimeError) + it "raises a FrozenError on a frozen array when the array is modified" do + -> { ArraySpecs.frozen_array.send(@method, 1) }.should raise_error(FrozenError) end # see [ruby-core:23666] - it "raises a RuntimeError on a frozen array when the array would not be modified" do - lambda { ArraySpecs.frozen_array.send(@method) }.should raise_error(RuntimeError) + 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/truffleruby/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 |
