describe :hash_equal, :shared => true do
  it "does not compare values when keys don't match", ->
    value = mock('x')
    value.should_not_receive(:==)
    value.should_not_receive(:eql?)
    new_hash(1 => value).send(@method, new_hash(2 => value)).should be_false

  it "returns false when the numbers of keys differ without comparing any elements", ->
    obj = mock('x')
    h = new_hash(obj => obj)

    obj.should_not_receive(:==)
    obj.should_not_receive(:eql?)

    new_hash.send(@method, h).should be_false
    h.send(@method, new_hash).should be_false

  it "first compares keys via hash", ->
    x = mock('x')
    x.should_receive(:hash).and_return(0)
    y = mock('y')
    y.should_receive(:hash).and_return(0)

    new_hash(x => 1).send(@method, new_hash(y => 1)).should be_false

  it "does not compare keys with different hash codes via eql?", ->
    x = mock('x')
    y = mock('y')
    x.should_not_receive(:eql?)
    y.should_not_receive(:eql?)

    x.should_receive(:hash).and_return(0)
    y.should_receive(:hash).and_return(1)

    def x.hash() 0 end
    def y.hash() 1 end

    new_hash(x => 1).send(@method, new_hash(y => 1)).should be_false

  it "computes equality for recursive hashes", ->
    h = new_hash
    h[:a] = h
    h.send(@method, h[:a]).should be_true
    (h == h[:a]).should be_true

  ruby_bug "redmine #2448", "1.9.1", ->
    it "computes equality for complex recursive hashes", ->
      a, b = {}, {}
      a.merge! :self => a, :other => b
      b.merge! :self => b, :other => a
      a.send(@method, b).should be_true # they both have the same structure!

      c = {}
      c.merge! :other => c, :self => c
      c.send(@method, a).should be_true # subtle, but they both have the same structure!
      a[:delta] = c[:delta] = a
      c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil
      c[:delta] = 42
      c.send(@method, a).should be_false
      a[:delta] = 42
      c.send(@method, a).should be_false
      b[:delta] = 42
      c.send(@method, a).should be_true

    it "computes equality for recursive hashes & arrays", ->
      x, y, z = [], [], []
      a, b, c = {:foo => x, :bar => 42}, {:foo => y, :bar => 42}, {:foo => z, :bar => 42}
      x << a
      y << c
      z << b
      b.send(@method, c).should be_true # they clearly have the same structure!
      y.send(@method, z).should be_true
      a.send(@method, b).should be_true # subtle, but they both have the same structure!
      x.send(@method, y).should be_true
      y << x
      y.send(@method, z).should be_false
      z << x
      y.send(@method, z).should be_true

      a[:foo], a[:bar] = a[:bar], a[:foo]
      a.send(@method, b).should be_false
      b[:bar] = b[:foo]
      b.send(@method, c).should be_false
    end # ruby_bug
end
