diff options
Diffstat (limited to 'spec/ruby/core/array/uniq_spec.rb')
| -rw-r--r-- | spec/ruby/core/array/uniq_spec.rb | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/spec/ruby/core/array/uniq_spec.rb b/spec/ruby/core/array/uniq_spec.rb new file mode 100644 index 0000000000..0289bee7c2 --- /dev/null +++ b/spec/ruby/core/array/uniq_spec.rb @@ -0,0 +1,243 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/iterable_and_tolerating_size_increasing' + +describe "Array#uniq" do + it "returns an array with no duplicates" do + ["a", "a", "b", "b", "c"].uniq.should == ["a", "b", "c"] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.uniq.should == [empty] + + array = ArraySpecs.recursive_array + array.uniq.should == [1, 'two', 3.0, array] + end + + it "uses eql? semantics" do + [1.0, 1].uniq.should == [1.0, 1] + end + + it "compares elements first with hash" do + x = mock('0') + x.should_receive(:hash).at_least(1).and_return(0) + y = mock('0') + y.should_receive(:hash).at_least(1).and_return(0) + + [x, y].uniq.should == [x, y] + end + + it "does not compare elements with different hash codes via eql?" do + x = mock('0') + x.should_not_receive(:eql?) + y = mock('1') + y.should_not_receive(:eql?) + + x.should_receive(:hash).at_least(1).and_return(0) + y.should_receive(:hash).at_least(1).and_return(1) + + [x, y].uniq.should == [x, y] + end + + it "compares elements with matching hash codes with #eql?" do + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) + + def obj.eql?(o) + false + end + + obj + end + + a.uniq.should == a + + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) + + def obj.eql?(o) + true + end + + obj + end + + a.uniq.size.should == 1 + end + + it "compares elements based on the value returned from the block" do + a = [1, 2, 3, 4] + a.uniq { |x| x >= 2 ? 1 : 0 }.should == [1, 2] + end + + it "yields items in order" do + a = [1, 2, 3] + yielded = [] + a.uniq { |v| yielded << v } + yielded.should == a + end + + it "handles nil and false like any other values" do + [nil, false, 42].uniq { :foo }.should == [nil] + [false, nil, 42].uniq { :bar }.should == [false] + end + + it "returns Array instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].uniq.should.instance_of?(Array) + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + [x, x].uniq.should == [x] + end + + describe "given an array of BasicObject subclasses that define ==, eql?, and hash" do + # jruby/jruby#3227 + it "filters equivalent elements using those definitions" do + + basic = Class.new(BasicObject) do + attr_reader :x + + def initialize(x) + @x = x + end + + def ==(rhs) + @x == rhs.x + end + alias_method :eql?, :== + + def hash + @x.hash + end + end + + a = [basic.new(3), basic.new(2), basic.new(1), basic.new(4), basic.new(1), basic.new(2), basic.new(3)] + a.uniq.should == [basic.new(3), basic.new(2), basic.new(1), basic.new(4)] + end + end +end + +describe "Array#uniq" do + @value_to_return = -> e { e } + it_behaves_like :array_iterable_and_tolerating_size_increasing, :uniq +end + +describe "Array#uniq!" do + it "modifies the array in place" do + a = [ "a", "a", "b", "b", "c" ] + a.uniq! + a.should == ["a", "b", "c"] + end + + it "returns self" do + a = [ "a", "a", "b", "b", "c" ] + a.should.equal?(a.uniq!) + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty_dup = empty.dup + empty.uniq! + empty.should == empty_dup + + array = ArraySpecs.recursive_array + expected = array[0..3] + array.uniq! + array.should == expected + end + + it "compares elements first with hash" do + x = mock('0') + x.should_receive(:hash).at_least(1).and_return(0) + y = mock('0') + y.should_receive(:hash).at_least(1).and_return(0) + + a = [x, y] + a.uniq! + a.should == [x, y] + end + + it "does not compare elements with different hash codes via eql?" do + x = mock('0') + x.should_not_receive(:eql?) + y = mock('1') + y.should_not_receive(:eql?) + + x.should_receive(:hash).at_least(1).and_return(0) + y.should_receive(:hash).at_least(1).and_return(1) + + a = [x, y] + a.uniq! + a.should == [x, y] + end + + it "returns nil if no changes are made to the array" do + [ "a", "b", "c" ].uniq!.should == nil + end + + it "raises a FrozenError on a frozen array when the array is modified" do + dup_ary = [1, 1, 2] + dup_ary.freeze + -> { dup_ary.uniq! }.should.raise(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.uniq!}.should.raise(FrozenError) + -> { ArraySpecs.empty_frozen_array.uniq!}.should.raise(FrozenError) + end + + it "doesn't yield to the block on a frozen array" do + -> { ArraySpecs.frozen_array.uniq!{ raise RangeError, "shouldn't yield"}}.should.raise(FrozenError) + end + + it "compares elements based on the value returned from the block" do + a = [1, 2, 3, 4] + a.uniq! { |x| x >= 2 ? 1 : 0 }.should == [1, 2] + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + a = [x, x] + a.uniq! + a.should == [x] + end + + it "does not truncate the array is the block raises an exception" do + a = [1, 2, 3] + begin + a.send(@method) { raise StandardError, 'Oops' } + rescue + end + + a.should == [1, 2, 3] + end + + it "doesn't change array if error is raised" do + a = [1, 1, 2, 2, 3, 3, 4, 4] + begin + a.send(@method) do |e| + raise StandardError, 'Oops' if e == 3 + e + end + rescue StandardError + end + + a.should == [1, 1, 2, 2, 3, 3, 4, 4] + end +end + +describe "Array#uniq!" do + @value_to_return = -> e { e } + it_behaves_like :array_iterable_and_tolerating_size_increasing, :uniq! +end |
