summaryrefslogtreecommitdiff
path: root/spec/ruby/core/array/shared
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/array/shared')
-rw-r--r--spec/ruby/core/array/shared/clone.rb20
-rw-r--r--spec/ruby/core/array/shared/collect.rb141
-rw-r--r--spec/ruby/core/array/shared/delete_if.rb13
-rw-r--r--spec/ruby/core/array/shared/difference.rb78
-rw-r--r--spec/ruby/core/array/shared/enumeratorize.rb5
-rw-r--r--spec/ruby/core/array/shared/eql.rb92
-rw-r--r--spec/ruby/core/array/shared/index.rb41
-rw-r--r--spec/ruby/core/array/shared/inspect.rb107
-rw-r--r--spec/ruby/core/array/shared/intersection.rb85
-rw-r--r--spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb25
-rw-r--r--spec/ruby/core/array/shared/join.rb112
-rw-r--r--spec/ruby/core/array/shared/keep_if.rb95
-rw-r--r--spec/ruby/core/array/shared/length.rb11
-rw-r--r--spec/ruby/core/array/shared/push.rb33
-rw-r--r--spec/ruby/core/array/shared/replace.rb60
-rw-r--r--spec/ruby/core/array/shared/select.rb35
-rw-r--r--spec/ruby/core/array/shared/slice.rb857
-rw-r--r--spec/ruby/core/array/shared/union.rb79
-rw-r--r--spec/ruby/core/array/shared/unshift.rb64
19 files changed, 1953 insertions, 0 deletions
diff --git a/spec/ruby/core/array/shared/clone.rb b/spec/ruby/core/array/shared/clone.rb
new file mode 100644
index 0000000000..035b45ec99
--- /dev/null
+++ b/spec/ruby/core/array/shared/clone.rb
@@ -0,0 +1,20 @@
+describe :array_clone, shared: true do
+ it "returns an Array or a subclass instance" do
+ [].send(@method).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2].send(@method).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "produces a shallow copy where the references are directly copied" do
+ a = [mock('1'), mock('2')]
+ b = a.send @method
+ 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
+ a = [1, 2, 3, 4]
+ b = a.send @method
+ b.should == a
+ b.__id__.should_not == a.__id__
+ end
+end
diff --git a/spec/ruby/core/array/shared/collect.rb b/spec/ruby/core/array/shared/collect.rb
new file mode 100644
index 0000000000..030302ced6
--- /dev/null
+++ b/spec/ruby/core/array/shared/collect.rb
@@ -0,0 +1,141 @@
+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.should_not equal a
+ end
+
+ it "does not return subclass instance" do
+ ArraySpecs::MyArray[1, 2, 3].send(@method) { |x| x + 1 }.should be_an_instance_of(Array)
+ end
+
+ it "does not change self" do
+ a = ['a', 'b', 'c', 'd']
+ a.send(@method) { |i| i + '!' }
+ a.should == ['a', 'b', 'c', 'd']
+ end
+
+ it "returns the evaluated value of block if it broke in the block" do
+ a = ['a', 'b', 'c', 'd']
+ b = a.send(@method) {|i|
+ if i == 'c'
+ break 0
+ else
+ i + '!'
+ end
+ }
+ b.should == 0
+ end
+
+ it "returns an Enumerator when no block given" do
+ a = [1, 2, 3]
+ a.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "raises an ArgumentError when no block and with arguments" do
+ a = [1, 2, 3]
+ -> {
+ a.send(@method, :foo)
+ }.should raise_error(ArgumentError)
+ 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
+ it "replaces each element with the value returned by block" do
+ a = [7, 9, 3, 5]
+ a.send(@method) { |i| i - 1 }.should equal(a)
+ a.should == [6, 8, 2, 4]
+ end
+
+ it "returns self" do
+ a = [1, 2, 3, 4, 5]
+ b = a.send(@method) {|i| i+1 }
+ 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
+ a = ['a', 'b', 'c', 'd']
+ b = a.send(@method) {|i|
+ if i == 'c'
+ break 0
+ else
+ i + '!'
+ end
+ }
+ b.should == 0
+ a.should == ['a!', 'b!', 'c', 'd']
+ end
+
+ it "returns an Enumerator when no block given, and the enumerator can modify the original array" do
+ a = [1, 2, 3]
+ enum = a.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+ enum.each{|i| "#{i}!" }
+ a.should == ["1!", "2!", "3!"]
+ end
+
+ describe "when frozen" do
+ it "raises a FrozenError" do
+ -> { ArraySpecs.frozen_array.send(@method) {} }.should raise_error(FrozenError)
+ end
+
+ it "raises a FrozenError when empty" do
+ -> { ArraySpecs.empty_frozen_array.send(@method) {} }.should raise_error(FrozenError)
+ end
+
+ it "raises a FrozenError when calling #each on the returned Enumerator" do
+ enumerator = ArraySpecs.frozen_array.send(@method)
+ -> { enumerator.each {|x| x } }.should raise_error(FrozenError)
+ end
+
+ it "raises a FrozenError when calling #each on the returned Enumerator when empty" do
+ enumerator = ArraySpecs.empty_frozen_array.send(@method)
+ -> { 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
new file mode 100644
index 0000000000..a3fdcf4fac
--- /dev/null
+++ b/spec/ruby/core/array/shared/delete_if.rb
@@ -0,0 +1,13 @@
+describe :delete_if, shared: true do
+ before :each do
+ @object = [1,2,3]
+ end
+
+ 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/enumeratorize.rb b/spec/ruby/core/array/shared/enumeratorize.rb
new file mode 100644
index 0000000000..a19a5d3b9b
--- /dev/null
+++ b/spec/ruby/core/array/shared/enumeratorize.rb
@@ -0,0 +1,5 @@
+describe :enumeratorize, shared: true do
+ it "returns an Enumerator if no block given" do
+ [1,2].send(@method).should be_an_instance_of(Enumerator)
+ end
+end
diff --git a/spec/ruby/core/array/shared/eql.rb b/spec/ruby/core/array/shared/eql.rb
new file mode 100644
index 0000000000..b5d9128434
--- /dev/null
+++ b/spec/ruby/core/array/shared/eql.rb
@@ -0,0 +1,92 @@
+describe :array_eql, shared: true do
+ it "returns true if other is the same array" do
+ a = [1]
+ a.send(@method, a).should be_true
+ end
+
+ it "returns true if corresponding elements are #eql?" do
+ [].send(@method, []).should be_true
+ [1, 2, 3, 4].send(@method, [1, 2, 3, 4]).should be_true
+ end
+
+ it "returns false if other is shorter than self" do
+ [1, 2, 3, 4].send(@method, [1, 2, 3]).should be_false
+ end
+
+ it "returns false if other is longer than self" do
+ [1, 2, 3, 4].send(@method, [1, 2, 3, 4, 5]).should be_false
+ end
+
+ it "returns false immediately when sizes of the arrays differ" do
+ obj = mock('1')
+ obj.should_not_receive(@method)
+
+ [] .send(@method, [obj] ).should be_false
+ [obj] .send(@method, [] ).should be_false
+ end
+
+ it "handles well recursive arrays" do
+ a = ArraySpecs.empty_recursive_array
+ a .send(@method, [a] ).should be_true
+ a .send(@method, [[a]] ).should be_true
+ [a] .send(@method, a ).should be_true
+ [[a]] .send(@method, a ).should be_true
+ # These may be surprising, but no difference can be
+ # found between these arrays, so they are ==.
+ # There is no "path" that will lead to a difference
+ # (contrary to other examples below)
+
+ a2 = ArraySpecs.empty_recursive_array
+ a .send(@method, a2 ).should be_true
+ a .send(@method, [a2] ).should be_true
+ a .send(@method, [[a2]] ).should be_true
+ [a] .send(@method, a2 ).should be_true
+ [[a]] .send(@method, a2 ).should be_true
+
+ back = []
+ forth = [back]; back << forth;
+ back .send(@method, a ).should be_true
+
+ x = []; x << x << x
+ x .send(@method, a ).should be_false # since x.size != a.size
+ x .send(@method, [a, a] ).should be_false # since x[0].size != [a, a][0].size
+ x .send(@method, [x, a] ).should be_false # since x[1].size != [x, a][1].size
+ [x, a] .send(@method, [a, x] ).should be_false # etc...
+ x .send(@method, [x, x] ).should be_true
+ x .send(@method, [[x, x], [x, x]] ).should be_true
+
+ tree = [];
+ branch = []; branch << tree << tree; tree << branch
+ tree2 = [];
+ branch2 = []; branch2 << tree2 << tree2; tree2 << branch2
+ forest = [tree, branch, :bird, a]; forest << forest
+ forest2 = [tree2, branch2, :bird, a2]; forest2 << forest2
+
+ forest .send(@method, forest2 ).should be_true
+ forest .send(@method, [tree2, branch, :bird, a, forest2]).should be_true
+
+ diffforest = [branch2, tree2, :bird, a2]; diffforest << forest2
+ forest .send(@method, diffforest ).should be_false # since forest[0].size == 1 != 3 == diffforest[0]
+ forest .send(@method, [nil] ).should be_false
+ forest .send(@method, [forest] ).should be_false
+ end
+
+ it "does not call #to_ary on its argument" do
+ obj = mock('to_ary')
+ obj.should_not_receive(:to_ary)
+
+ [1, 2, 3].send(@method, obj).should be_false
+ end
+
+ it "does not call #to_ary on Array subclasses" do
+ ary = ArraySpecs::ToAryArray[5, 6, 7]
+ ary.should_not_receive(:to_ary)
+ [5, 6, 7].send(@method, ary).should be_true
+ end
+
+ it "ignores array class differences" do
+ ArraySpecs::MyArray[1, 2, 3].send(@method, [1, 2, 3]).should be_true
+ ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_true
+ [1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_true
+ end
+end
diff --git a/spec/ruby/core/array/shared/index.rb b/spec/ruby/core/array/shared/index.rb
new file mode 100644
index 0000000000..a4a0adbab6
--- /dev/null
+++ b/spec/ruby/core/array/shared/index.rb
@@ -0,0 +1,41 @@
+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')
+ def x.==(obj) 3 == obj; end
+
+ [2, x, 3, 1, 3, 1].send(@method, 3).should == 1
+ [2, 3.0, 3, x, 1, 3, 1].send(@method, x).should == 1
+ end
+
+ it "returns 0 if first element == to object" do
+ [2, 1, 3, 2, 5].send(@method, 2).should == 0
+ end
+
+ it "returns size-1 if only last element == to object" do
+ [2, 1, 3, 1, 5].send(@method, 5).should == 4
+ end
+
+ it "returns nil if no element == to object" do
+ [2, 1, 1, 1, 1].send(@method, 3).should == nil
+ end
+
+ it "accepts a block instead of an argument" do
+ [4, 2, 1, 5, 1, 3].send(@method) {|x| x < 2}.should == 2
+ end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ [4, 2, 1, 5, 1, 3].send(@method, 5) {|x| x < 2}.should == 3
+ }.should complain(/given block not used/)
+ end
+
+ describe "given no argument and no block" do
+ it "produces an Enumerator" 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
new file mode 100644
index 0000000000..af5128c645
--- /dev/null
+++ b/spec/ruby/core/array/shared/inspect.rb
@@ -0,0 +1,107 @@
+require_relative '../fixtures/encoded_strings'
+
+describe :array_inspect, shared: true do
+ it "returns a string" do
+ [1, 2, 3].send(@method).should be_an_instance_of(String)
+ end
+
+ it "returns '[]' for an empty Array" do
+ [].send(@method).should == "[]"
+ end
+
+ it "calls inspect on its elements and joins the results with commas" do
+ items = Array.new(3) do |i|
+ obj = mock(i.to_s)
+ obj.should_receive(:inspect).and_return(i.to_s)
+ obj
+ end
+ items.send(@method).should == "[0, 1, 2]"
+ end
+
+ it "does not call #to_s on a String returned from #inspect" do
+ str = +"abc"
+ str.should_not_receive(:to_s)
+
+ [str].send(@method).should == '["abc"]'
+ end
+
+ it "calls #to_s on the object returned from #inspect if the Object isn't a String" do
+ obj = mock("Array#inspect/to_s calls #to_s")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return("abc")
+
+ [obj].send(@method).should == "[abc]"
+ end
+
+ it "does not call #to_str on the object returned from #inspect when it is not a String" do
+ obj = mock("Array#inspect/to_s does not call #to_str")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_not_receive(:to_str)
+
+ [obj].send(@method).should =~ /^\[#<MockObject:0x[0-9a-f]+>\]$/
+ end
+
+ it "does not call #to_str on the object returned from #to_s when it is not a String" do
+ obj = mock("Array#inspect/to_s does not call #to_str on #to_s result")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return(obj)
+ obj.should_not_receive(:to_str)
+
+ [obj].send(@method).should =~ /^\[#<MockObject:0x[0-9a-f]+>\]$/
+ end
+
+ it "does not swallow exceptions raised by #to_s" do
+ obj = mock("Array#inspect/to_s does not swallow #to_s exceptions")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_raise(Exception)
+
+ -> { [obj].send(@method) }.should raise_error(Exception)
+ end
+
+ it "represents a recursive element with '[...]'" do
+ ArraySpecs.recursive_array.send(@method).should == "[1, \"two\", 3.0, [...], [...], [...], [...], [...]]"
+ ArraySpecs.head_recursive_array.send(@method).should == "[[...], [...], [...], [...], [...], 1, \"two\", 3.0]"
+ ArraySpecs.empty_recursive_array.send(@method).should == "[[...]]"
+ end
+
+ describe "with encoding" do
+ before :each do
+ @default_external_encoding = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @default_external_encoding
+ end
+
+ it "returns a US-ASCII string for an empty Array" do
+ [].send(@method).encoding.should == Encoding::US_ASCII
+ end
+
+ it "use the default external encoding if it is ascii compatible" do
+ Encoding.default_external = Encoding.find('UTF-8')
+
+ utf8 = "utf8".encode("UTF-8")
+ jp = "jp".encode("EUC-JP")
+ array = [jp, utf8]
+
+ array.send(@method).encoding.name.should == "UTF-8"
+ end
+
+ it "use US-ASCII encoding if the default external encoding is not ascii compatible" do
+ Encoding.default_external = Encoding.find('UTF-32')
+
+ utf8 = "utf8".encode("UTF-8")
+ jp = "jp".encode("EUC-JP")
+ array = [jp, utf8]
+
+ array.send(@method).encoding.name.should == "US-ASCII"
+ 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].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
new file mode 100644
index 0000000000..507b13e3c8
--- /dev/null
+++ b/spec/ruby/core/array/shared/join.rb
@@ -0,0 +1,112 @@
+require_relative '../fixtures/classes'
+require_relative '../fixtures/encoded_strings'
+
+describe :array_join_with_default_separator, shared: true do
+ before :each do
+ @separator = $,
+ end
+
+ after :each do
+ $, = @separator
+ end
+
+ it "returns an empty string if the Array is empty" do
+ [].send(@method).should == ''
+ end
+
+ it "returns a US-ASCII string for an empty Array" do
+ [].send(@method).encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a string formed by concatenating each String element separated by $," do
+ suppress_warning {
+ $, = " | "
+ ["1", "2", "3"].send(@method).should == "1 | 2 | 3"
+ }
+ end
+
+ it "attempts coercion via #to_str first" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return("foo")
+ [obj].send(@method).should == "foo"
+ end
+
+ it "attempts coercion via #to_ary second" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_ary).any_number_of_times.and_return(["foo"])
+ [obj].send(@method).should == "foo"
+ end
+
+ it "attempts coercion via #to_s third" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_ary).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_s).any_number_of_times.and_return("foo")
+ [obj].send(@method).should == "foo"
+ end
+
+ 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
+ -> { [1, obj].send(@method) }.should raise_error(NoMethodError)
+ end
+
+ it "raises an ArgumentError when the Array is recursive" do
+ -> { 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_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
+ ary3.send(@method).encoding.should == Encoding::UTF_8
+ ary4.send(@method).encoding.should == Encoding::US_ASCII
+ end
+
+ it "uses the widest common encoding when other strings are incompatible" do
+ ary1 = ArraySpecs.array_with_utf8_and_usascii_strings
+ ary2 = ArraySpecs.array_with_usascii_and_utf8_strings
+
+ ary1.send(@method).encoding.should == Encoding::UTF_8
+ ary2.send(@method).encoding.should == Encoding::UTF_8
+ end
+
+ it "fails for arrays with incompatibly-encoded strings" do
+ ary_utf8_bad_binary = ArraySpecs.array_with_utf8_and_binary_strings
+
+ -> { 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
+
+describe :array_join_with_string_separator, shared: true do
+ it "returns a string formed by concatenating each element.to_str separated by separator" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).and_return("foo")
+ [1, 2, 3, 4, obj].send(@method, ' | ').should == '1 | 2 | 3 | 4 | foo'
+ end
+
+ it "uses the same separator with nested arrays" 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
+end
diff --git a/spec/ruby/core/array/shared/keep_if.rb b/spec/ruby/core/array/shared/keep_if.rb
new file mode 100644
index 0000000000..43a047c0a7
--- /dev/null
+++ b/spec/ruby/core/array/shared/keep_if.rb
@@ -0,0 +1,95 @@
+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
+ array = [1, 2, 3, 4, 5]
+ array.send(@method) {|item| item > 3 }.should equal(array)
+ array.should == [4, 5]
+ end
+
+ it "returns an enumerator if no block is given" do
+ [1, 2, 3].send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "updates the receiver after all blocks" do
+ a = [1, 2, 3]
+ a.send(@method) do |e|
+ a.length.should == 3
+ false
+ end
+ a.length.should == 0
+ end
+
+ before :all do
+ @object = [1,2,3]
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+
+ describe "on frozen objects" do
+ before :each do
+ @origin = [true, false]
+ @frozen = @origin.dup.freeze
+ end
+
+ it "returns an Enumerator if no block is given" do
+ @frozen.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ describe "with truthy block" do
+ it "keeps elements after any exception" do
+ -> { @frozen.send(@method) { true } }.should raise_error(Exception)
+ @frozen.should == @origin
+ end
+
+ 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
+ -> { @frozen.send(@method) { false } }.should raise_error(Exception)
+ @frozen.should == @origin
+ end
+
+ 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/length.rb b/spec/ruby/core/array/shared/length.rb
new file mode 100644
index 0000000000..f84966d0ba
--- /dev/null
+++ b/spec/ruby/core/array/shared/length.rb
@@ -0,0 +1,11 @@
+describe :array_length, shared: true do
+ it "returns the number of elements" do
+ [].send(@method).should == 0
+ [1, 2, 3].send(@method).should == 3
+ end
+
+ it "properly handles recursive arrays" do
+ ArraySpecs.empty_recursive_array.send(@method).should == 1
+ ArraySpecs.recursive_array.send(@method).should == 8
+ end
+end
diff --git a/spec/ruby/core/array/shared/push.rb b/spec/ruby/core/array/shared/push.rb
new file mode 100644
index 0000000000..ac790fb6a4
--- /dev/null
+++ b/spec/ruby/core/array/shared/push.rb
@@ -0,0 +1,33 @@
+describe :array_push, shared: true do
+ it "appends the arguments to the array" do
+ a = [ "a", "b", "c" ]
+ a.send(@method, "d", "e", "f").should equal(a)
+ a.send(@method).should == ["a", "b", "c", "d", "e", "f"]
+ a.send(@method, 5)
+ a.should == ["a", "b", "c", "d", "e", "f", 5]
+
+ a = [0, 1]
+ a.send(@method, 2)
+ a.should == [0, 1, 2]
+ end
+
+ it "isn't confused by previous shift" do
+ a = [ "a", "b", "c" ]
+ a.shift
+ a.send(@method, "foo")
+ a.should == ["b", "c", "foo"]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.send(@method, :last).should == [empty, :last]
+
+ array = ArraySpecs.recursive_array
+ array.send(@method, :last).should == [1, 'two', 3.0, array, array, array, array, array, :last]
+ end
+
+ 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
new file mode 100644
index 0000000000..9a6e60c1b0
--- /dev/null
+++ b/spec/ruby/core/array/shared/replace.rb
@@ -0,0 +1,60 @@
+describe :array_replace, shared: true do
+ it "replaces the elements with elements from other array" do
+ a = [1, 2, 3, 4, 5]
+ b = ['a', 'b', 'c']
+ a.send(@method, b).should equal(a)
+ a.should == b
+ a.should_not equal(b)
+
+ a.send(@method, [4] * 10)
+ a.should == [4] * 10
+
+ a.send(@method, [])
+ a.should == []
+ end
+
+ it "properly handles recursive arrays" do
+ orig = [1, 2, 3]
+ empty = ArraySpecs.empty_recursive_array
+ orig.send(@method, empty)
+ orig.should == empty
+
+ array = ArraySpecs.recursive_array
+ orig.send(@method, array)
+ orig.should == array
+ end
+
+ it "returns self" do
+ ary = [1, 2, 3]
+ other = [:a, :b, :c]
+ ary.send(@method, other).should equal(ary)
+ end
+
+ it "does not make self dependent to the original array" do
+ ary = [1, 2, 3]
+ other = [:a, :b, :c]
+ ary.send(@method, other)
+ ary.should == [:a, :b, :c]
+ ary << :d
+ ary.should == [:a, :b, :c, :d]
+ other.should == [:a, :b, :c]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('to_ary')
+ obj.stub!(:to_ary).and_return([1, 2, 3])
+ [].send(@method, obj).should == [1, 2, 3]
+ end
+
+ it "does not call #to_ary on Array subclasses" do
+ obj = ArraySpecs::ToAryArray[5, 6, 7]
+ obj.should_not_receive(:to_ary)
+ [].send(@method, ArraySpecs::ToAryArray[5, 6, 7]).should == [5, 6, 7]
+ end
+
+ it "raises a FrozenError on a frozen array" do
+ -> {
+ ArraySpecs.frozen_array.send(@method, ArraySpecs.frozen_array)
+ }.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
new file mode 100644
index 0000000000..b80261d32f
--- /dev/null
+++ b/spec/ruby/core/array/shared/slice.rb
@@ -0,0 +1,857 @@
+describe :array_slice, shared: true do
+ it "returns the element at index with [index]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 1).should == "b"
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, 0).should == 1
+ a.send(@method, 1).should == 2
+ a.send(@method, 2).should == 3
+ a.send(@method, 3).should == 4
+ a.send(@method, 4).should == nil
+ a.send(@method, 10).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the element at index from the end of the array with [-index]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, -2).should == "d"
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, -1).should == 4
+ a.send(@method, -2).should == 3
+ a.send(@method, -3).should == 2
+ a.send(@method, -4).should == 1
+ a.send(@method, -5).should == nil
+ a.send(@method, -10).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns count elements starting from index with [index, count]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 2, 3).should == ["c", "d", "e"]
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, 0, 0).should == []
+ a.send(@method, 0, 1).should == [1]
+ a.send(@method, 0, 2).should == [1, 2]
+ a.send(@method, 0, 4).should == [1, 2, 3, 4]
+ a.send(@method, 0, 6).should == [1, 2, 3, 4]
+ a.send(@method, 0, -1).should == nil
+ a.send(@method, 0, -2).should == nil
+ a.send(@method, 0, -4).should == nil
+
+ a.send(@method, 2, 0).should == []
+ a.send(@method, 2, 1).should == [3]
+ a.send(@method, 2, 2).should == [3, 4]
+ a.send(@method, 2, 4).should == [3, 4]
+ a.send(@method, 2, -1).should == nil
+
+ a.send(@method, 4, 0).should == []
+ a.send(@method, 4, 2).should == []
+ a.send(@method, 4, -1).should == nil
+
+ a.send(@method, 5, 0).should == nil
+ a.send(@method, 5, 2).should == nil
+ a.send(@method, 5, -1).should == nil
+
+ a.send(@method, 6, 0).should == nil
+ a.send(@method, 6, 2).should == nil
+ a.send(@method, 6, -1).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns count elements starting at index from the end of array with [-index, count]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, -2, 2).should == ["d", "e"]
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, -1, 0).should == []
+ a.send(@method, -1, 1).should == [4]
+ a.send(@method, -1, 2).should == [4]
+ a.send(@method, -1, -1).should == nil
+
+ a.send(@method, -2, 0).should == []
+ a.send(@method, -2, 1).should == [3]
+ a.send(@method, -2, 2).should == [3, 4]
+ a.send(@method, -2, 4).should == [3, 4]
+ a.send(@method, -2, -1).should == nil
+
+ a.send(@method, -4, 0).should == []
+ a.send(@method, -4, 1).should == [1]
+ a.send(@method, -4, 2).should == [1, 2]
+ a.send(@method, -4, 4).should == [1, 2, 3, 4]
+ a.send(@method, -4, 6).should == [1, 2, 3, 4]
+ a.send(@method, -4, -1).should == nil
+
+ a.send(@method, -5, 0).should == nil
+ a.send(@method, -5, 1).should == nil
+ a.send(@method, -5, 10).should == nil
+ a.send(@method, -5, -1).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the first count elements with [0, count]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 0, 3).should == ["a", "b", "c"]
+ end
+
+ it "returns the subarray which is independent to self with [index,count]" do
+ a = [1, 2, 3]
+ sub = a.send(@method, 1,2)
+ sub.replace([:a, :b])
+ a.should == [1, 2, 3]
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.stub!(:to_int).and_return(2)
+
+ a = [1, 2, 3, 4]
+ a.send(@method, obj).should == 3
+ a.send(@method, obj, 1).should == [3]
+ a.send(@method, obj, obj).should == [3, 4]
+ 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']
+ [ "a", "b", "c", "d", "e" ].send(@method, 3..3).should == ['d']
+ [ "a", "b", "c", "d", "e" ].send(@method, 3..-2).should == ['d']
+ ['a'].send(@method, 0..-1).should == ['a']
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, 0..-10).should == []
+ a.send(@method, 0..0).should == [1]
+ a.send(@method, 0..1).should == [1, 2]
+ a.send(@method, 0..2).should == [1, 2, 3]
+ a.send(@method, 0..3).should == [1, 2, 3, 4]
+ a.send(@method, 0..4).should == [1, 2, 3, 4]
+ a.send(@method, 0..10).should == [1, 2, 3, 4]
+
+ a.send(@method, 2..-10).should == []
+ a.send(@method, 2..0).should == []
+ a.send(@method, 2..2).should == [3]
+ a.send(@method, 2..3).should == [3, 4]
+ a.send(@method, 2..4).should == [3, 4]
+
+ a.send(@method, 3..0).should == []
+ a.send(@method, 3..3).should == [4]
+ a.send(@method, 3..4).should == [4]
+
+ a.send(@method, 4..0).should == []
+ a.send(@method, 4..4).should == []
+ a.send(@method, 4..5).should == []
+
+ a.send(@method, 5..0).should == nil
+ a.send(@method, 5..5).should == nil
+ a.send(@method, 5..6).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns elements specified by Range indexes except the element at index n with [m...n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 1...3).should == ["b", "c"]
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, 0...-10).should == []
+ a.send(@method, 0...0).should == []
+ a.send(@method, 0...1).should == [1]
+ a.send(@method, 0...2).should == [1, 2]
+ a.send(@method, 0...3).should == [1, 2, 3]
+ a.send(@method, 0...4).should == [1, 2, 3, 4]
+ a.send(@method, 0...10).should == [1, 2, 3, 4]
+
+ a.send(@method, 2...-10).should == []
+ a.send(@method, 2...0).should == []
+ a.send(@method, 2...2).should == []
+ a.send(@method, 2...3).should == [3]
+ a.send(@method, 2...4).should == [3, 4]
+
+ a.send(@method, 3...0).should == []
+ a.send(@method, 3...3).should == []
+ a.send(@method, 3...4).should == [4]
+
+ a.send(@method, 4...0).should == []
+ a.send(@method, 4...4).should == []
+ a.send(@method, 4...5).should == []
+
+ a.send(@method, 5...0).should == nil
+ a.send(@method, 5...5).should == nil
+ a.send(@method, 5...6).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns elements that exist if range start is in the array but range end is not with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 4..7).should == ["e"]
+ end
+
+ it "accepts Range instances having a negative m and both signs for n with [m..n] and [m...n]" do
+ a = [1, 2, 3, 4]
+
+ a.send(@method, -1..-1).should == [4]
+ a.send(@method, -1...-1).should == []
+ a.send(@method, -1..3).should == [4]
+ a.send(@method, -1...3).should == []
+ a.send(@method, -1..4).should == [4]
+ a.send(@method, -1...4).should == [4]
+ a.send(@method, -1..10).should == [4]
+ a.send(@method, -1...10).should == [4]
+ a.send(@method, -1..0).should == []
+ a.send(@method, -1..-4).should == []
+ a.send(@method, -1...-4).should == []
+ a.send(@method, -1..-6).should == []
+ a.send(@method, -1...-6).should == []
+
+ a.send(@method, -2..-2).should == [3]
+ a.send(@method, -2...-2).should == []
+ a.send(@method, -2..-1).should == [3, 4]
+ a.send(@method, -2...-1).should == [3]
+ a.send(@method, -2..10).should == [3, 4]
+ a.send(@method, -2...10).should == [3, 4]
+
+ a.send(@method, -4..-4).should == [1]
+ a.send(@method, -4..-2).should == [1, 2, 3]
+ a.send(@method, -4...-2).should == [1, 2]
+ a.send(@method, -4..-1).should == [1, 2, 3, 4]
+ a.send(@method, -4...-1).should == [1, 2, 3]
+ a.send(@method, -4..3).should == [1, 2, 3, 4]
+ a.send(@method, -4...3).should == [1, 2, 3]
+ a.send(@method, -4..4).should == [1, 2, 3, 4]
+ a.send(@method, -4...4).should == [1, 2, 3, 4]
+ a.send(@method, -4...4).should == [1, 2, 3, 4]
+ a.send(@method, -4..0).should == [1]
+ a.send(@method, -4...0).should == []
+ a.send(@method, -4..1).should == [1, 2]
+ a.send(@method, -4...1).should == [1]
+
+ a.send(@method, -5..-5).should == nil
+ a.send(@method, -5...-5).should == nil
+ a.send(@method, -5..-4).should == nil
+ a.send(@method, -5..-1).should == nil
+ a.send(@method, -5..10).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the subarray which is independent to self with [m..n]" do
+ a = [1, 2, 3]
+ sub = a.send(@method, 1..2)
+ sub.replace([:a, :b])
+ a.should == [1, 2, 3]
+ end
+
+ it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" 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
+
+ def from.to_int() 1 end
+ def to.to_int() -2 end
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, from..to).should == [2, 3]
+ a.send(@method, from...to).should == [2]
+ a.send(@method, 1..0).should == []
+ a.send(@method, 1...0).should == []
+
+ -> { 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
+ a = [1, 2, 3, 4]
+ range_incl = ArraySpecs::MyRange.new(1, 2)
+ range_excl = ArraySpecs::MyRange.new(-3, -1, true)
+
+ a.send(@method, range_incl).should == [2, 3]
+ a.send(@method, range_excl).should == [2, 3]
+ end
+
+ it "returns nil for a requested index not in the array with [index]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 5).should == nil
+ end
+
+ it "returns [] if the index is valid but length is zero with [index, length]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 0, 0).should == []
+ [ "a", "b", "c", "d", "e" ].send(@method, 2, 0).should == []
+ end
+
+ it "returns nil if length is zero but index is invalid with [index, length]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 100, 0).should == nil
+ [ "a", "b", "c", "d", "e" ].send(@method, -50, 0).should == nil
+ end
+
+ # This is by design. It is in the official documentation.
+ it "returns [] if index == array.size with [index, length]" do
+ %w|a b c d e|.send(@method, 5, 2).should == []
+ end
+
+ it "returns nil if index > array.size with [index, length]" do
+ %w|a b c d e|.send(@method, 6, 2).should == nil
+ end
+
+ it "returns nil if length is negative with [index, length]" do
+ %w|a b c d e|.send(@method, 3, -1).should == nil
+ %w|a b c d e|.send(@method, 2, -2).should == nil
+ %w|a b c d e|.send(@method, 1, -100).should == nil
+ end
+
+ it "returns nil if no requested index is in the array with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 6..10).should == nil
+ end
+
+ it "returns nil if range start is not in the array with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, -10..2).should == nil
+ [ "a", "b", "c", "d", "e" ].send(@method, 10..12).should == nil
+ end
+
+ it "returns an empty array when m == n with [m...n]" do
+ [1, 2, 3, 4, 5].send(@method, 1...1).should == []
+ end
+
+ it "returns an empty array with [0...0]" do
+ [1, 2, 3, 4, 5].send(@method, 0...0).should == []
+ end
+
+ it "returns a subarray where m, n negatives and m < n with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, -3..-2).should == ["c", "d"]
+ end
+
+ it "returns an array containing the first element with [0..0]" do
+ [1, 2, 3, 4, 5].send(@method, 0..0).should == [1]
+ end
+
+ it "returns the entire array with [0..-1]" do
+ [1, 2, 3, 4, 5].send(@method, 0..-1).should == [1, 2, 3, 4, 5]
+ end
+
+ it "returns all but the last element with [0...-1]" do
+ [1, 2, 3, 4, 5].send(@method, 0...-1).should == [1, 2, 3, 4]
+ end
+
+ it "returns [3] for [2..-1] out of [1, 2, 3]" do
+ [1,2,3].send(@method, 2..-1).should == [3]
+ end
+
+ it "returns an empty array when m > n and m, n are positive with [m..n]" do
+ [1, 2, 3, 4, 5].send(@method, 3..2).should == []
+ end
+
+ it "returns an empty array when m > n and m, n are negative with [m..n]" do
+ [1, 2, 3, 4, 5].send(@method, -2..-3).should == []
+ end
+
+ it "does not expand array when the indices are outside of the array bounds" do
+ a = [1, 2]
+ a.send(@method, 4).should == nil
+ a.should == [1, 2]
+ a.send(@method, 4, 0).should == nil
+ a.should == [1, 2]
+ a.send(@method, 6, 1).should == nil
+ a.should == [1, 2]
+ a.send(@method, 8...8).should == nil
+ a.should == [1, 2]
+ a.send(@method, 10..10).should == nil
+ a.should == [1, 2]
+ end
+
+ describe "with a subclass of Array" do
+ before :each do
+ ScratchPad.clear
+
+ @array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
+ end
+
+ 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, 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 an empty array when m == n with [m...n]" do
+ @array.send(@method, 1...1).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns an empty array with [0...0]" do
+ @array.send(@method, 0...0).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns an empty array when m > n and m, n are positive with [m..n]" do
+ @array.send(@method, 3..2).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns an empty array when m > n and m, n are negative with [m..n]" do
+ @array.send(@method, -2..-3).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns [] if index == array.size with [index, length]" do
+ @array.send(@method, 5, 2).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns [] if the index is valid but length is zero with [index, length]" do
+ @array.send(@method, 0, 0).should == []
+ @array.send(@method, 2, 0).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "does not call #initialize on the subclass instance" do
+ @array.send(@method, 0, 3).should == [1, 2, 3]
+ ScratchPad.recorded.should be_nil
+ end
+ end
+
+ 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(bignum_value)
+ -> { array.send(@method, obj) }.should raise_error(RangeError)
+
+ obj = 8e19
+ -> { 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(bignum_value)
+ -> { array.send(@method, 1, obj) }.should raise_error(RangeError)
+
+ obj = 8e19
+ -> { 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
new file mode 100644
index 0000000000..9e0fe7556a
--- /dev/null
+++ b/spec/ruby/core/array/shared/unshift.rb
@@ -0,0 +1,64 @@
+describe :array_unshift, shared: true do
+ it "prepends object to the original array" do
+ a = [1, 2, 3]
+ a.send(@method, "a").should equal(a)
+ a.should == ['a', 1, 2, 3]
+ a.send(@method).should equal(a)
+ a.should == ['a', 1, 2, 3]
+ a.send(@method, 5, 4, 3)
+ a.should == [5, 4, 3, 'a', 1, 2, 3]
+
+ # shift all but one element
+ a = [1, 2]
+ a.shift
+ a.send(@method, 3, 4)
+ a.should == [3, 4, 2]
+
+ # now shift all elements
+ a.shift
+ a.shift
+ a.shift
+ a.send(@method, 3, 4)
+ 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
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.send(@method, :new).should == [:new, empty]
+
+ array = ArraySpecs.recursive_array
+ array.send(@method, :new)
+ array[0..5].should == [:new, 1, 'two', 3.0, array, array]
+ end
+
+ 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 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