require "test/unit"
require 'set'

class A
  def foo
    "F"
  end
 
  def self.bar
    "B"
  end
end
 
class B < A
end

class Empty
end

class PublicMethodsTest < Test::Unit::TestCase

  def assert_set(a, b, message='')
    # I think this might be weird behaviour in ruby trunk that should be fixed too..
    # some of the introspection methods return symbols, some strings..
    a = a.map { |x| x.to_s.to_sym }
    b = b.map { |x| x.to_s.to_sym }
    diff = Set.new(a) ^ Set.new(b)
    assert diff.length == 0, "Differences: #{diff.to_a.join ", "}. #{message}"
  end

  def test_sanity
    assert_raise MiniTest::Assertion do
      assert_set [1,2], [2,3]
    end
    assert_raise MiniTest::Assertion do
      assert_set [1], [1,2,3]
    end
    assert_set [1,2], [1,2]

  end

  def test_instance_methods
    assert_set A.new.public_methods, A.public_instance_methods
    assert_set B.new.public_methods, B.public_instance_methods
    assert_set B.new.public_methods(false), B.public_instance_methods(false)
    assert_set A.new.public_methods(false), A.public_instance_methods(false)
  end

  def test_compare_instance_methods
    # calling public_methods on instances behaves as expected:
    assert_set Empty.new.public_methods(false), []
    assert_set A.new.public_methods(false), ["foo"]
    assert_set B.new.public_methods(false), []
  end

  def test_this_is_what_i_expect
    # In my understanding of the interface, this is what /should/ happen
    # But I can see having ['new', 'superclass', 'allocate'] might be acceptable
    assert_set Empty.public_methods(false), []
    assert_set A.public_methods(false), ["bar"]
    assert_set B.public_methods(false), []
  end

  def test_consistent_behaviour
    # With the always-present methods, then behaviour should at least be consistent...
    
    # So these two are consistent:
    assert_set A.new.public_methods(false), Empty.new.public_methods(false) + ['foo']
    assert_set A.public_methods(false), Empty.public_methods(false) + ['bar']

    # But here, they are not:
    assert_set B.new.public_methods(false), Empty.new.public_methods(false)
    assert_set B.public_methods(false), Empty.public_methods(false), '"bar" is inherited from A, but doesn\'t include methods inherited from Object!'
  end

end
