django/tests/gis_tests/test_ptr.py

74 lines
2.3 KiB
Python

import ctypes
from unittest import mock
from django.contrib.gis.ptr import CPointerBase
from django.test import SimpleTestCase
class CPointerBaseTests(SimpleTestCase):
def test(self):
destructor_mock = mock.Mock()
class NullPointerException(Exception):
pass
class FakeGeom1(CPointerBase):
null_ptr_exception_class = NullPointerException
class FakeGeom2(FakeGeom1):
ptr_type = ctypes.POINTER(ctypes.c_float)
destructor = destructor_mock
fg1 = FakeGeom1()
fg2 = FakeGeom2()
# These assignments are OK. None is allowed because it's equivalent
# to the NULL pointer.
fg1.ptr = fg1.ptr_type()
fg1.ptr = None
fg2.ptr = fg2.ptr_type(ctypes.c_float(5.23))
fg2.ptr = None
# Because pointers have been set to NULL, an exception is raised on
# access. Raising an exception is preferable to a segmentation fault
# that commonly occurs when a C method is given a NULL reference.
for fg in (fg1, fg2):
with self.assertRaises(NullPointerException):
fg.ptr
# Anything that's either not None or the acceptable pointer type
# results in a TypeError when trying to assign it to the `ptr` property.
# Thus, memory addresses (integers) and pointers of the incorrect type
# (in `bad_ptrs`) aren't allowed.
bad_ptrs = (5, ctypes.c_char_p(b"foobar"))
for bad_ptr in bad_ptrs:
for fg in (fg1, fg2):
with self.assertRaisesMessage(TypeError, "Incompatible pointer type"):
fg.ptr = bad_ptr
# Object can be deleted without a destructor set.
fg = FakeGeom1()
fg.ptr = fg.ptr_type(1)
del fg
# A NULL pointer isn't passed to the destructor.
fg = FakeGeom2()
fg.ptr = None
del fg
self.assertFalse(destructor_mock.called)
# The destructor is called if set.
fg = FakeGeom2()
ptr = fg.ptr_type(ctypes.c_float(1.0))
fg.ptr = ptr
del fg
destructor_mock.assert_called_with(ptr)
def test_destructor_catches_importerror(self):
class FakeGeom(CPointerBase):
destructor = mock.Mock(side_effect=ImportError)
fg = FakeGeom()
fg.ptr = fg.ptr_type(1)
del fg