from ctypes import *

import unittest



# IMPORTANT INFO:

#

# Consider this call:

#    func.restype = c_char_p

#    func(c_char_p("123"))

# It returns

#    "123"

#

# WHY IS THIS SO?

#

# argument tuple (c_char_p("123"), ) is destroyed after the function

# func is called, but NOT before the result is actually built.

#

# If the arglist would be destroyed BEFORE the result has been built,

# the c_char_p("123") object would already have a zero refcount,

# and the pointer passed to (and returned by) the function would

# probably point to deallocated space.

#

# In this case, there would have to be an additional reference to the argument...



import _ctypes_test

testdll = CDLL(_ctypes_test.__file__)



# Return machine address `a` as a (possibly long) non-negative integer.

# Starting with Python 2.5, id(anything) is always non-negative, and

# the ctypes addressof() inherits that via PyLong_FromVoidPtr().

def positive_address(a):

    if a >= 0:

        return a

    # View the bits in `a` as unsigned instead.

    import struct

    num_bits = struct.calcsize("P") * 8 # num bits in native machine address

    a += 1L << num_bits

    assert a >= 0

    return a



def c_wbuffer(init):

    n = len(init) + 1

    return (c_wchar * n)(*init)



class CharPointersTestCase(unittest.TestCase):



    def setUp(self):

        func = testdll._testfunc_p_p

        func.restype = c_long

        func.argtypes = None



    def test_paramflags(self):

        # function returns c_void_p result,

        # and has a required parameter named 'input'

        prototype = CFUNCTYPE(c_void_p, c_void_p)

        func = prototype(("_testfunc_p_p", testdll),

                         ((1, "input"),))



        try:

            func()

        except TypeError, details:

            self.failUnlessEqual(str(details), "required argument 'input' missing")

        else:

            self.fail("TypeError not raised")



        self.failUnlessEqual(func(None), None)

        self.failUnlessEqual(func(input=None), None)





    def test_int_pointer_arg(self):

        func = testdll._testfunc_p_p

        func.restype = c_long

        self.failUnlessEqual(0, func(0))



        ci = c_int(0)



        func.argtypes = POINTER(c_int),

        self.failUnlessEqual(positive_address(addressof(ci)),

                             positive_address(func(byref(ci))))



        func.argtypes = c_char_p,

        self.assertRaises(ArgumentError, func, byref(ci))



        func.argtypes = POINTER(c_short),

        self.assertRaises(ArgumentError, func, byref(ci))



        func.argtypes = POINTER(c_double),

        self.assertRaises(ArgumentError, func, byref(ci))



    def test_POINTER_c_char_arg(self):

        func = testdll._testfunc_p_p

        func.restype = c_char_p

        func.argtypes = POINTER(c_char),



        self.failUnlessEqual(None, func(None))

        self.failUnlessEqual("123", func("123"))

        self.failUnlessEqual(None, func(c_char_p(None)))

        self.failUnlessEqual("123", func(c_char_p("123")))



        self.failUnlessEqual("123", func(c_buffer("123")))

        ca = c_char("a")

        self.failUnlessEqual("a", func(pointer(ca))[0])

        self.failUnlessEqual("a", func(byref(ca))[0])



    def test_c_char_p_arg(self):

        func = testdll._testfunc_p_p

        func.restype = c_char_p

        func.argtypes = c_char_p,



        self.failUnlessEqual(None, func(None))

        self.failUnlessEqual("123", func("123"))

        self.failUnlessEqual(None, func(c_char_p(None)))

        self.failUnlessEqual("123", func(c_char_p("123")))



        self.failUnlessEqual("123", func(c_buffer("123")))

        ca = c_char("a")

        self.failUnlessEqual("a", func(pointer(ca))[0])

        self.failUnlessEqual("a", func(byref(ca))[0])



    def test_c_void_p_arg(self):

        func = testdll._testfunc_p_p

        func.restype = c_char_p

        func.argtypes = c_void_p,



        self.failUnlessEqual(None, func(None))

        self.failUnlessEqual("123", func("123"))

        self.failUnlessEqual("123", func(c_char_p("123")))

        self.failUnlessEqual(None, func(c_char_p(None)))



        self.failUnlessEqual("123", func(c_buffer("123")))

        ca = c_char("a")

        self.failUnlessEqual("a", func(pointer(ca))[0])

        self.failUnlessEqual("a", func(byref(ca))[0])



        func(byref(c_int()))

        func(pointer(c_int()))

        func((c_int * 3)())



        try:

            func.restype = c_wchar_p

        except NameError:

            pass

        else:

            self.failUnlessEqual(None, func(c_wchar_p(None)))

            self.failUnlessEqual(u"123", func(c_wchar_p(u"123")))



    def test_instance(self):

        func = testdll._testfunc_p_p

        func.restype = c_void_p



        class X:

            _as_parameter_ = None



        func.argtypes = c_void_p,

        self.failUnlessEqual(None, func(X()))



        func.argtypes = None

        self.failUnlessEqual(None, func(X()))



try:

    c_wchar

except NameError:

    pass

else:

    class WCharPointersTestCase(unittest.TestCase):



        def setUp(self):

            func = testdll._testfunc_p_p

            func.restype = c_int

            func.argtypes = None





        def test_POINTER_c_wchar_arg(self):

            func = testdll._testfunc_p_p

            func.restype = c_wchar_p

            func.argtypes = POINTER(c_wchar),



            self.failUnlessEqual(None, func(None))

            self.failUnlessEqual(u"123", func(u"123"))

            self.failUnlessEqual(None, func(c_wchar_p(None)))

            self.failUnlessEqual(u"123", func(c_wchar_p(u"123")))



            self.failUnlessEqual(u"123", func(c_wbuffer(u"123")))

            ca = c_wchar("a")

            self.failUnlessEqual(u"a", func(pointer(ca))[0])

            self.failUnlessEqual(u"a", func(byref(ca))[0])



        def test_c_wchar_p_arg(self):

            func = testdll._testfunc_p_p

            func.restype = c_wchar_p

            func.argtypes = c_wchar_p,



            c_wchar_p.from_param(u"123")



            self.failUnlessEqual(None, func(None))

            self.failUnlessEqual("123", func(u"123"))

            self.failUnlessEqual(None, func(c_wchar_p(None)))

            self.failUnlessEqual("123", func(c_wchar_p("123")))



            # XXX Currently, these raise TypeErrors, although they shouldn't:

            self.failUnlessEqual("123", func(c_wbuffer("123")))

            ca = c_wchar("a")

            self.failUnlessEqual("a", func(pointer(ca))[0])

            self.failUnlessEqual("a", func(byref(ca))[0])



class ArrayTest(unittest.TestCase):

    def test(self):

        func = testdll._testfunc_ai8

        func.restype = POINTER(c_int)

        func.argtypes = c_int * 8,



        func((c_int * 8)(1, 2, 3, 4, 5, 6, 7, 8))



        # This did crash before:



        def func(): pass

        CFUNCTYPE(None, c_int * 3)(func)



################################################################



if __name__ == '__main__':

    unittest.main()

