diff options
Diffstat (limited to 'spec/ruby/core/array/shared')
| -rw-r--r-- | spec/ruby/core/array/shared/clone.rb | 20 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/delete_if.rb | 13 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/difference.rb | 78 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/enumeratorize.rb | 5 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/eql.rb | 92 | ||||
| -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 | 15 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/keep_if.rb | 95 | ||||
| -rw-r--r-- | spec/ruby/core/array/shared/union.rb | 79 |
10 files changed, 507 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..1a45c2fe2c --- /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.instance_of?(Array) + ArraySpecs::MyArray[1, 2].send(@method).should.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/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..3fe22331bd --- /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(TypeError) + end + + it "does not return subclass instance for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].send(@method, []).should.instance_of?(Array) + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[]).should.instance_of?(Array) + [1, 2, 3].send(@method, ArraySpecs::MyArray[]).should.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..5beab5c4c4 --- /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.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..5e770bf167 --- /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 == true + end + + it "returns true if corresponding elements are #eql?" do + [].send(@method, []).should == true + [1, 2, 3, 4].send(@method, [1, 2, 3, 4]).should == true + end + + it "returns false if other is shorter than self" do + [1, 2, 3, 4].send(@method, [1, 2, 3]).should == false + end + + it "returns false if other is longer than self" do + [1, 2, 3, 4].send(@method, [1, 2, 3, 4, 5]).should == 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 == false + [obj] .send(@method, [] ).should == false + end + + it "handles well recursive arrays" do + a = ArraySpecs.empty_recursive_array + a .send(@method, [a] ).should == true + a .send(@method, [[a]] ).should == true + [a] .send(@method, a ).should == true + [[a]] .send(@method, a ).should == 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 == true + a .send(@method, [a2] ).should == true + a .send(@method, [[a2]] ).should == true + [a] .send(@method, a2 ).should == true + [[a]] .send(@method, a2 ).should == true + + back = [] + forth = [back]; back << forth; + back .send(@method, a ).should == true + + x = []; x << x << x + x .send(@method, a ).should == false # since x.size != a.size + x .send(@method, [a, a] ).should == false # since x[0].size != [a, a][0].size + x .send(@method, [x, a] ).should == false # since x[1].size != [x, a][1].size + [x, a] .send(@method, [a, x] ).should == false # etc... + x .send(@method, [x, x] ).should == true + x .send(@method, [[x, x], [x, x]] ).should == 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 == true + forest .send(@method, [tree2, branch, :bird, a, forest2]).should == true + + diffforest = [branch2, tree2, :bird, a2]; diffforest << forest2 + forest .send(@method, diffforest ).should == false # since forest[0].size == 1 != 3 == diffforest[0] + forest .send(@method, [nil] ).should == false + forest .send(@method, [forest] ).should == 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 == 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 == true + end + + it "ignores array class differences" do + ArraySpecs::MyArray[1, 2, 3].send(@method, [1, 2, 3]).should == true + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should == true + [1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should == true + 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..dda72e8bd7 --- /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.instance_of?(Array) + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should.instance_of?(Array) + [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should.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..93d5329ee3 --- /dev/null +++ b/spec/ruby/core/array/shared/join.rb @@ -0,0 +1,15 @@ +require_relative '../fixtures/classes' +require_relative '../fixtures/encoded_strings' + +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..44625eebd1 --- /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.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.instance_of?(Enumerator) + end + + describe "with truthy block" do + it "keeps elements after any exception" do + -> { @frozen.send(@method) { true } }.should.raise(Exception) + @frozen.should == @origin + end + + it "raises a FrozenError" do + -> { @frozen.send(@method) { true } }.should.raise(FrozenError) + end + end + + describe "with falsy block" do + it "keeps elements after any exception" do + -> { @frozen.send(@method) { false } }.should.raise(Exception) + @frozen.should == @origin + end + + it "raises a FrozenError" do + -> { @frozen.send(@method) { false } }.should.raise(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(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/union.rb b/spec/ruby/core/array/shared/union.rb new file mode 100644 index 0000000000..0b225b9a31 --- /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.instance_of?(Array) + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should.instance_of?(Array) + [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should.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 |
