1. import ctypes
    
  2. from unittest import mock
    
  3. 
    
  4. from django.contrib.gis.ptr import CPointerBase
    
  5. from django.test import SimpleTestCase
    
  6. 
    
  7. 
    
  8. class CPointerBaseTests(SimpleTestCase):
    
  9.     def test(self):
    
  10.         destructor_mock = mock.Mock()
    
  11. 
    
  12.         class NullPointerException(Exception):
    
  13.             pass
    
  14. 
    
  15.         class FakeGeom1(CPointerBase):
    
  16.             null_ptr_exception_class = NullPointerException
    
  17. 
    
  18.         class FakeGeom2(FakeGeom1):
    
  19.             ptr_type = ctypes.POINTER(ctypes.c_float)
    
  20.             destructor = destructor_mock
    
  21. 
    
  22.         fg1 = FakeGeom1()
    
  23.         fg2 = FakeGeom2()
    
  24. 
    
  25.         # These assignments are OK. None is allowed because it's equivalent
    
  26.         # to the NULL pointer.
    
  27.         fg1.ptr = fg1.ptr_type()
    
  28.         fg1.ptr = None
    
  29.         fg2.ptr = fg2.ptr_type(ctypes.c_float(5.23))
    
  30.         fg2.ptr = None
    
  31. 
    
  32.         # Because pointers have been set to NULL, an exception is raised on
    
  33.         # access. Raising an exception is preferable to a segmentation fault
    
  34.         # that commonly occurs when a C method is given a NULL reference.
    
  35.         for fg in (fg1, fg2):
    
  36.             with self.assertRaises(NullPointerException):
    
  37.                 fg.ptr
    
  38. 
    
  39.         # Anything that's either not None or the acceptable pointer type
    
  40.         # results in a TypeError when trying to assign it to the `ptr` property.
    
  41.         # Thus, memory addresses (integers) and pointers of the incorrect type
    
  42.         # (in `bad_ptrs`) aren't allowed.
    
  43.         bad_ptrs = (5, ctypes.c_char_p(b"foobar"))
    
  44.         for bad_ptr in bad_ptrs:
    
  45.             for fg in (fg1, fg2):
    
  46.                 with self.assertRaisesMessage(TypeError, "Incompatible pointer type"):
    
  47.                     fg.ptr = bad_ptr
    
  48. 
    
  49.         # Object can be deleted without a destructor set.
    
  50.         fg = FakeGeom1()
    
  51.         fg.ptr = fg.ptr_type(1)
    
  52.         del fg
    
  53. 
    
  54.         # A NULL pointer isn't passed to the destructor.
    
  55.         fg = FakeGeom2()
    
  56.         fg.ptr = None
    
  57.         del fg
    
  58.         self.assertFalse(destructor_mock.called)
    
  59. 
    
  60.         # The destructor is called if set.
    
  61.         fg = FakeGeom2()
    
  62.         ptr = fg.ptr_type(ctypes.c_float(1.0))
    
  63.         fg.ptr = ptr
    
  64.         del fg
    
  65.         destructor_mock.assert_called_with(ptr)
    
  66. 
    
  67.     def test_destructor_catches_importerror(self):
    
  68.         class FakeGeom(CPointerBase):
    
  69.             destructor = mock.Mock(side_effect=ImportError)
    
  70. 
    
  71.         fg = FakeGeom()
    
  72.         fg.ptr = fg.ptr_type(1)
    
  73.         del fg