require_relative 'test_base'
require 'dl/func'

module DL
  class TestCFunc < TestBase
    def setup
      super
      @name = 'strcpy'
      @cf = CFunc.new(@libc[@name], TYPE_VOIDP, @name)
    end

    def test_ptr=
      @cf.ptr = @libc['malloc']
      assert_equal @cf.ptr, @libc['malloc']
    end

    def test_ptr
      assert_equal @cf.ptr, @libc[@name]
    end

    def test_set_calltype
      @cf.calltype = :foo
      assert_equal :foo, @cf.calltype
    end

    def test_new_ptr_type_name
      assert_equal @name, @cf.name
      assert @cf.name.tainted?, 'name should be tainted'
      assert_equal :cdecl, @cf.calltype
      assert_equal TYPE_VOIDP, @cf.ctype
    end

    def test_new_ptr
      cf = CFunc.new(@libc['strcpy'])
      assert_nil cf.name
      assert_equal :cdecl, cf.calltype
      assert_equal TYPE_VOID, cf.ctype
    end

    def test_name_should_be_duped
      assert_equal @name, @cf.name
      assert @cf.name.tainted?, 'name should be tainted'

      name = @name.dup
      @name << 'foo'

      assert_equal name, @cf.name
    end

    def test_to_s
      s = @cf.to_s
      assert s.tainted?, 'to_s should be tainted'
      assert_match(/ptr=#{sprintf("0x0*%x", @cf.ptr)}/, s)
      assert_match(/name='#{@cf.name}'/, s)
      assert_match(/type=#{@cf.ctype}/, s)
    end

    def test_inspect
      assert_equal @cf.inspect, @cf.to_s
    end

    def test_inspect_is_tainted
      assert @cf.inspect.tainted?, 'inspect is tainted'
    end

    def test_to_i
      assert_equal @cf.to_i, @cf.ptr
      assert_equal @libc[@name], @cf.to_i
    end

    def test_last_error
      Thread.new do
        f = Function.new(@cf, [TYPE_VOIDP, TYPE_VOIDP])
        assert_nil CFunc.last_error
        f.call("000", "123")
        assert_not_nil CFunc.last_error
      end.join
    end
  end
end
