require 'runit/cui/testrunner'
require 'runit/testcase'

# A unit test suite for public, protected, private, and secret instance
# methods.
#
# TODO: Write tests for class methods.
#

class Foo
  include RUNIT::Assert
public
  def public_foo
    return :foo
  end

protected
  def protected_foo
    return :foo
  end

private
  def private_foo
    return :foo
  end

secret
  def secret_foo
    return :foo
  end

public
  def public_self_test
    self.public_foo
  end

  def protected_self_test
    self.protected_foo
  end

  def private_self_test
    assert_exception(NoMethodError) do
      self.private_foo
    end
  end

  def secret_self_test
    assert_exception(NoMethodError) do
      self.secret_foo
    end
  end

  def public_test
    public_foo
  end

  def protected_test
    protected_foo
  end

  def private_test
    private_foo
  end

  def secret_test
    secret_foo
  end
end

class Bar < Foo
  def bar_public_self_test
    self.public_foo
  end

  def bar_protected_self_test
    self.protected_foo
  end

  def bar_private_self_test
    assert_exception(NoMethodError) do
      self.private_foo
    end
  end

  def bar_secret_self_test
    assert_exception(NoMethodError) do
      self.secret_foo
    end
  end

  def bar_public_test
    public_foo
  end

  def bar_protected_test
    protected_foo
  end

  def bar_private_test
    private_foo
  end

  def bar_secret_test
    assert_exception(NoMethodError) do
      secret_foo
    end
  end
end

class SafeMethodTest < RUNIT::TestCase
  def test_public_access
    f = Foo.new
    
    f.public_foo();
    
    assert_exception(NoMethodError) do
      f.protected_foo()
    end
    
    assert_exception(NoMethodError) do
      f.private_foo()
    end
    
    assert_exception(NoMethodError) do
      f.secret_foo()
    end
  end

  def test_same_class_access
    f = Foo.new
    f.public_methods.each do |m|
      if m =~ /_test/ then
        f.__send__(m)
      end
    end
  end
  
  def test_subclass_access
    b = Bar.new
    b.public_methods.each do |m|
      if m =~ /_test/ then
        b.__send__(m)
      end
    end
  end

  def test_instance_methods
    foo_pub_im = Foo.public_instance_methods
    foo_prot_im = Foo.protected_instance_methods
    foo_priv_im = Foo.private_instance_methods
    foo_secr_im = Foo.secret_instance_methods
    assert_not_nil foo_pub_im.index("public_foo")
    assert_not_nil foo_prot_im.index("protected_foo")
    assert_not_nil foo_secr_im.index("secret_foo")
    assert_nil foo_secr_im.index("public_foo")
  end

  def test_subclass_instance_methods
    bar_pub_im = Bar.public_instance_methods
    bar_prot_im = Bar.protected_instance_methods
    bar_priv_im = Bar.private_instance_methods
    bar_secr_im = Bar.secret_instance_methods
    assert_nil bar_pub_im.index("public_foo")
    assert_nil bar_prot_im.index("protected_foo")
    assert_nil bar_secr_im.index("secret_foo")
    assert_nil bar_secr_im.index("public_foo")
  end

  def test_methods
    f = Foo.new
    foo_pub = f.public_methods
    foo_prot = f.protected_methods
    foo_priv = f.private_methods
    foo_secr = f.secret_methods
    assert_not_nil foo_pub.index("public_foo")
    assert_not_nil foo_prot.index("protected_foo")
    assert_not_nil foo_secr.index("secret_foo")
    assert_nil foo_secr.index("public_foo")
  end

  def test_subclass_methods
    b = Bar.new
    bar_pub = b.public_methods
    bar_prot = b.protected_methods
    bar_priv = b.private_methods
    bar_secr = b.secret_methods
    assert_not_nil bar_pub.index("public_foo")
    assert_not_nil bar_prot.index("protected_foo")
    assert_not_nil bar_secr.index("secret_foo")
    assert_nil bar_secr.index("public_foo")
  end
end

RUNIT::CUI::TestRunner.run(SafeMethodTest.suite)

