mirror of
https://github.com/python/cpython.git
synced 2025-08-02 08:02:56 +00:00
gh-124570: ctypes: Run some Structure tests on Union as well (GH-124976)
- Move some Structure tests to test_structunion; use a common base test class + two subclasses to run them on Union too - Remove test_union for now as it's redundant Note: `test_simple_structs` & `test_simple_unions` are in the common file because they share `formats`.
This commit is contained in:
parent
c914212474
commit
01fc3b34cc
3 changed files with 368 additions and 357 deletions
353
Lib/test/test_ctypes/test_structunion.py
Normal file
353
Lib/test/test_ctypes/test_structunion.py
Normal file
|
@ -0,0 +1,353 @@
|
|||
"""Common tests for ctypes.Structure and ctypes.Union"""
|
||||
|
||||
import unittest
|
||||
from ctypes import (Structure, Union, POINTER, sizeof, alignment,
|
||||
c_char, c_byte, c_ubyte,
|
||||
c_short, c_ushort, c_int, c_uint,
|
||||
c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double)
|
||||
from ._support import (_CData, PyCStructType, UnionType,
|
||||
Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
||||
Py_TPFLAGS_IMMUTABLETYPE)
|
||||
from struct import calcsize
|
||||
|
||||
|
||||
class StructUnionTestBase:
|
||||
formats = {"c": c_char,
|
||||
"b": c_byte,
|
||||
"B": c_ubyte,
|
||||
"h": c_short,
|
||||
"H": c_ushort,
|
||||
"i": c_int,
|
||||
"I": c_uint,
|
||||
"l": c_long,
|
||||
"L": c_ulong,
|
||||
"q": c_longlong,
|
||||
"Q": c_ulonglong,
|
||||
"f": c_float,
|
||||
"d": c_double,
|
||||
}
|
||||
|
||||
def test_subclass(self):
|
||||
class X(self.cls):
|
||||
_fields_ = [("a", c_int)]
|
||||
|
||||
class Y(X):
|
||||
_fields_ = [("b", c_int)]
|
||||
|
||||
class Z(X):
|
||||
pass
|
||||
|
||||
self.assertEqual(sizeof(X), sizeof(c_int))
|
||||
self.check_sizeof(Y,
|
||||
struct_size=sizeof(c_int)*2,
|
||||
union_size=sizeof(c_int))
|
||||
self.assertEqual(sizeof(Z), sizeof(c_int))
|
||||
self.assertEqual(X._fields_, [("a", c_int)])
|
||||
self.assertEqual(Y._fields_, [("b", c_int)])
|
||||
self.assertEqual(Z._fields_, [("a", c_int)])
|
||||
|
||||
def test_subclass_delayed(self):
|
||||
class X(self.cls):
|
||||
pass
|
||||
self.assertEqual(sizeof(X), 0)
|
||||
X._fields_ = [("a", c_int)]
|
||||
|
||||
class Y(X):
|
||||
pass
|
||||
self.assertEqual(sizeof(Y), sizeof(X))
|
||||
Y._fields_ = [("b", c_int)]
|
||||
|
||||
class Z(X):
|
||||
pass
|
||||
|
||||
self.assertEqual(sizeof(X), sizeof(c_int))
|
||||
self.check_sizeof(Y,
|
||||
struct_size=sizeof(c_int)*2,
|
||||
union_size=sizeof(c_int))
|
||||
self.assertEqual(sizeof(Z), sizeof(c_int))
|
||||
self.assertEqual(X._fields_, [("a", c_int)])
|
||||
self.assertEqual(Y._fields_, [("b", c_int)])
|
||||
self.assertEqual(Z._fields_, [("a", c_int)])
|
||||
|
||||
def test_inheritance_hierarchy(self):
|
||||
self.assertEqual(self.cls.mro(), [self.cls, _CData, object])
|
||||
self.assertEqual(type(self.metacls), type)
|
||||
|
||||
def test_type_flags(self):
|
||||
for cls in self.cls, self.metacls:
|
||||
with self.subTest(cls=cls):
|
||||
self.assertTrue(cls.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
|
||||
self.assertFalse(cls.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
|
||||
|
||||
def test_metaclass_details(self):
|
||||
# Abstract classes (whose metaclass __init__ was not called) can't be
|
||||
# instantiated directly
|
||||
NewClass = self.metacls.__new__(self.metacls, 'NewClass',
|
||||
(self.cls,), {})
|
||||
for cls in self.cls, NewClass:
|
||||
with self.subTest(cls=cls):
|
||||
with self.assertRaisesRegex(TypeError, "abstract class"):
|
||||
obj = cls()
|
||||
|
||||
# Cannot call the metaclass __init__ more than once
|
||||
class T(self.cls):
|
||||
_fields_ = [("x", c_char),
|
||||
("y", c_char)]
|
||||
with self.assertRaisesRegex(SystemError, "already initialized"):
|
||||
self.metacls.__init__(T, 'ptr', (), {})
|
||||
|
||||
def test_alignment(self):
|
||||
class X(self.cls):
|
||||
_fields_ = [("x", c_char * 3)]
|
||||
self.assertEqual(alignment(X), calcsize("s"))
|
||||
self.assertEqual(sizeof(X), calcsize("3s"))
|
||||
|
||||
class Y(self.cls):
|
||||
_fields_ = [("x", c_char * 3),
|
||||
("y", c_int)]
|
||||
self.assertEqual(alignment(Y), alignment(c_int))
|
||||
self.check_sizeof(Y,
|
||||
struct_size=calcsize("3s i"),
|
||||
union_size=max(calcsize("3s"), calcsize("i")))
|
||||
|
||||
class SI(self.cls):
|
||||
_fields_ = [("a", X),
|
||||
("b", Y)]
|
||||
self.assertEqual(alignment(SI), max(alignment(Y), alignment(X)))
|
||||
self.check_sizeof(SI,
|
||||
struct_size=calcsize("3s0i 3si 0i"),
|
||||
union_size=max(calcsize("3s"), calcsize("i")))
|
||||
|
||||
class IS(self.cls):
|
||||
_fields_ = [("b", Y),
|
||||
("a", X)]
|
||||
|
||||
self.assertEqual(alignment(SI), max(alignment(X), alignment(Y)))
|
||||
self.check_sizeof(IS,
|
||||
struct_size=calcsize("3si 3s 0i"),
|
||||
union_size=max(calcsize("3s"), calcsize("i")))
|
||||
|
||||
class XX(self.cls):
|
||||
_fields_ = [("a", X),
|
||||
("b", X)]
|
||||
self.assertEqual(alignment(XX), alignment(X))
|
||||
self.check_sizeof(XX,
|
||||
struct_size=calcsize("3s 3s 0s"),
|
||||
union_size=calcsize("3s"))
|
||||
|
||||
def test_empty(self):
|
||||
# I had problems with these
|
||||
#
|
||||
# Although these are pathological cases: Empty Structures!
|
||||
class X(self.cls):
|
||||
_fields_ = []
|
||||
|
||||
# Is this really the correct alignment, or should it be 0?
|
||||
self.assertTrue(alignment(X) == 1)
|
||||
self.assertTrue(sizeof(X) == 0)
|
||||
|
||||
class XX(self.cls):
|
||||
_fields_ = [("a", X),
|
||||
("b", X)]
|
||||
|
||||
self.assertEqual(alignment(XX), 1)
|
||||
self.assertEqual(sizeof(XX), 0)
|
||||
|
||||
def test_fields(self):
|
||||
# test the offset and size attributes of Structure/Union fields.
|
||||
class X(self.cls):
|
||||
_fields_ = [("x", c_int),
|
||||
("y", c_char)]
|
||||
|
||||
self.assertEqual(X.x.offset, 0)
|
||||
self.assertEqual(X.x.size, sizeof(c_int))
|
||||
|
||||
if self.cls == Structure:
|
||||
self.assertEqual(X.y.offset, sizeof(c_int))
|
||||
else:
|
||||
self.assertEqual(X.y.offset, 0)
|
||||
self.assertEqual(X.y.size, sizeof(c_char))
|
||||
|
||||
# readonly
|
||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "offset", 92)
|
||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "size", 92)
|
||||
|
||||
# XXX Should we check nested data types also?
|
||||
# offset is always relative to the class...
|
||||
|
||||
def test_invalid_field_types(self):
|
||||
class POINT(self.cls):
|
||||
pass
|
||||
self.assertRaises(TypeError, setattr, POINT, "_fields_", [("x", 1), ("y", 2)])
|
||||
|
||||
def test_invalid_name(self):
|
||||
# field name must be string
|
||||
def declare_with_name(name):
|
||||
class S(self.cls):
|
||||
_fields_ = [(name, c_int)]
|
||||
|
||||
self.assertRaises(TypeError, declare_with_name, b"x")
|
||||
|
||||
def test_intarray_fields(self):
|
||||
class SomeInts(self.cls):
|
||||
_fields_ = [("a", c_int * 4)]
|
||||
|
||||
# can use tuple to initialize array (but not list!)
|
||||
self.assertEqual(SomeInts((1, 2)).a[:], [1, 2, 0, 0])
|
||||
self.assertEqual(SomeInts((1, 2)).a[::], [1, 2, 0, 0])
|
||||
self.assertEqual(SomeInts((1, 2)).a[::-1], [0, 0, 2, 1])
|
||||
self.assertEqual(SomeInts((1, 2)).a[::2], [1, 0])
|
||||
self.assertEqual(SomeInts((1, 2)).a[1:5:6], [2])
|
||||
self.assertEqual(SomeInts((1, 2)).a[6:4:-1], [])
|
||||
self.assertEqual(SomeInts((1, 2, 3, 4)).a[:], [1, 2, 3, 4])
|
||||
self.assertEqual(SomeInts((1, 2, 3, 4)).a[::], [1, 2, 3, 4])
|
||||
# too long
|
||||
# XXX Should raise ValueError?, not RuntimeError
|
||||
self.assertRaises(RuntimeError, SomeInts, (1, 2, 3, 4, 5))
|
||||
|
||||
def test_huge_field_name(self):
|
||||
# issue12881: segfault with large structure field names
|
||||
def create_class(length):
|
||||
class S(self.cls):
|
||||
_fields_ = [('x' * length, c_int)]
|
||||
|
||||
for length in [10 ** i for i in range(0, 8)]:
|
||||
try:
|
||||
create_class(length)
|
||||
except MemoryError:
|
||||
# MemoryErrors are OK, we just don't want to segfault
|
||||
pass
|
||||
|
||||
def test_abstract_class(self):
|
||||
class X(self.cls):
|
||||
_abstract_ = "something"
|
||||
with self.assertRaisesRegex(TypeError, r"^abstract class$"):
|
||||
X()
|
||||
|
||||
def test_methods(self):
|
||||
self.assertIn("in_dll", dir(type(self.cls)))
|
||||
self.assertIn("from_address", dir(type(self.cls)))
|
||||
self.assertIn("in_dll", dir(type(self.cls)))
|
||||
|
||||
|
||||
class StructureTestCase(unittest.TestCase, StructUnionTestBase):
|
||||
cls = Structure
|
||||
metacls = PyCStructType
|
||||
|
||||
def test_metaclass_name(self):
|
||||
self.assertEqual(self.metacls.__name__, "PyCStructType")
|
||||
|
||||
def check_sizeof(self, cls, *, struct_size, union_size):
|
||||
self.assertEqual(sizeof(cls), struct_size)
|
||||
|
||||
def test_simple_structs(self):
|
||||
for code, tp in self.formats.items():
|
||||
class X(Structure):
|
||||
_fields_ = [("x", c_char),
|
||||
("y", tp)]
|
||||
self.assertEqual((sizeof(X), code),
|
||||
(calcsize("c%c0%c" % (code, code)), code))
|
||||
|
||||
|
||||
class UnionTestCase(unittest.TestCase, StructUnionTestBase):
|
||||
cls = Union
|
||||
metacls = UnionType
|
||||
|
||||
def test_metaclass_name(self):
|
||||
self.assertEqual(self.metacls.__name__, "UnionType")
|
||||
|
||||
def check_sizeof(self, cls, *, struct_size, union_size):
|
||||
self.assertEqual(sizeof(cls), union_size)
|
||||
|
||||
def test_simple_unions(self):
|
||||
for code, tp in self.formats.items():
|
||||
class X(Union):
|
||||
_fields_ = [("x", c_char),
|
||||
("y", tp)]
|
||||
self.assertEqual((sizeof(X), code),
|
||||
(calcsize("%c" % (code)), code))
|
||||
|
||||
|
||||
class PointerMemberTestBase:
|
||||
def test(self):
|
||||
# a Structure/Union with a POINTER field
|
||||
class S(self.cls):
|
||||
_fields_ = [("array", POINTER(c_int))]
|
||||
|
||||
s = S()
|
||||
# We can assign arrays of the correct type
|
||||
s.array = (c_int * 3)(1, 2, 3)
|
||||
items = [s.array[i] for i in range(3)]
|
||||
self.assertEqual(items, [1, 2, 3])
|
||||
|
||||
s.array[0] = 42
|
||||
|
||||
items = [s.array[i] for i in range(3)]
|
||||
self.assertEqual(items, [42, 2, 3])
|
||||
|
||||
s.array[0] = 1
|
||||
|
||||
items = [s.array[i] for i in range(3)]
|
||||
self.assertEqual(items, [1, 2, 3])
|
||||
|
||||
class PointerMemberTestCase_Struct(unittest.TestCase, PointerMemberTestBase):
|
||||
cls = Structure
|
||||
|
||||
def test_none_to_pointer_fields(self):
|
||||
class S(self.cls):
|
||||
_fields_ = [("x", c_int),
|
||||
("p", POINTER(c_int))]
|
||||
|
||||
s = S()
|
||||
s.x = 12345678
|
||||
s.p = None
|
||||
self.assertEqual(s.x, 12345678)
|
||||
|
||||
class PointerMemberTestCase_Union(unittest.TestCase, PointerMemberTestBase):
|
||||
cls = Union
|
||||
|
||||
def test_none_to_pointer_fields(self):
|
||||
class S(self.cls):
|
||||
_fields_ = [("x", c_int),
|
||||
("p", POINTER(c_int))]
|
||||
|
||||
s = S()
|
||||
s.x = 12345678
|
||||
s.p = None
|
||||
self.assertFalse(s.p) # NULL pointers are falsy
|
||||
|
||||
|
||||
class TestRecursiveBase:
|
||||
def test_contains_itself(self):
|
||||
class Recursive(self.cls):
|
||||
pass
|
||||
|
||||
try:
|
||||
Recursive._fields_ = [("next", Recursive)]
|
||||
except AttributeError as details:
|
||||
self.assertIn("Structure or union cannot contain itself",
|
||||
str(details))
|
||||
else:
|
||||
self.fail("Structure or union cannot contain itself")
|
||||
|
||||
|
||||
def test_vice_versa(self):
|
||||
class First(self.cls):
|
||||
pass
|
||||
class Second(self.cls):
|
||||
pass
|
||||
|
||||
First._fields_ = [("second", Second)]
|
||||
|
||||
try:
|
||||
Second._fields_ = [("first", First)]
|
||||
except AttributeError as details:
|
||||
self.assertIn("_fields_ is final", str(details))
|
||||
else:
|
||||
self.fail("AttributeError not raised")
|
||||
|
||||
class TestRecursiveStructure(unittest.TestCase, TestRecursiveBase):
|
||||
cls = Structure
|
||||
|
||||
class TestRecursiveUnion(unittest.TestCase, TestRecursiveBase):
|
||||
cls = Union
|
|
@ -1,209 +1,24 @@
|
|||
"""Tests for ctypes.Structure
|
||||
|
||||
Features common with Union should go in test_structunion.py instead.
|
||||
"""
|
||||
|
||||
from platform import architecture as _architecture
|
||||
import struct
|
||||
import sys
|
||||
import unittest
|
||||
from ctypes import (CDLL, Structure, Union, POINTER, sizeof, byref, alignment,
|
||||
from ctypes import (CDLL, Structure, Union, POINTER, sizeof, byref,
|
||||
c_void_p, c_char, c_wchar, c_byte, c_ubyte,
|
||||
c_uint8, c_uint16, c_uint32,
|
||||
c_short, c_ushort, c_int, c_uint,
|
||||
c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double)
|
||||
c_uint8, c_uint16, c_uint32, c_int, c_uint,
|
||||
c_long, c_ulong, c_longlong, c_float, c_double)
|
||||
from ctypes.util import find_library
|
||||
from struct import calcsize
|
||||
from collections import namedtuple
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
_ctypes_test = import_helper.import_module("_ctypes_test")
|
||||
from ._support import (_CData, PyCStructType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
||||
Py_TPFLAGS_IMMUTABLETYPE)
|
||||
|
||||
|
||||
class SubclassesTest(unittest.TestCase):
|
||||
def test_subclass(self):
|
||||
class X(Structure):
|
||||
_fields_ = [("a", c_int)]
|
||||
|
||||
class Y(X):
|
||||
_fields_ = [("b", c_int)]
|
||||
|
||||
class Z(X):
|
||||
pass
|
||||
|
||||
self.assertEqual(sizeof(X), sizeof(c_int))
|
||||
self.assertEqual(sizeof(Y), sizeof(c_int)*2)
|
||||
self.assertEqual(sizeof(Z), sizeof(c_int))
|
||||
self.assertEqual(X._fields_, [("a", c_int)])
|
||||
self.assertEqual(Y._fields_, [("b", c_int)])
|
||||
self.assertEqual(Z._fields_, [("a", c_int)])
|
||||
|
||||
def test_subclass_delayed(self):
|
||||
class X(Structure):
|
||||
pass
|
||||
self.assertEqual(sizeof(X), 0)
|
||||
X._fields_ = [("a", c_int)]
|
||||
|
||||
class Y(X):
|
||||
pass
|
||||
self.assertEqual(sizeof(Y), sizeof(X))
|
||||
Y._fields_ = [("b", c_int)]
|
||||
|
||||
class Z(X):
|
||||
pass
|
||||
|
||||
self.assertEqual(sizeof(X), sizeof(c_int))
|
||||
self.assertEqual(sizeof(Y), sizeof(c_int)*2)
|
||||
self.assertEqual(sizeof(Z), sizeof(c_int))
|
||||
self.assertEqual(X._fields_, [("a", c_int)])
|
||||
self.assertEqual(Y._fields_, [("b", c_int)])
|
||||
self.assertEqual(Z._fields_, [("a", c_int)])
|
||||
|
||||
|
||||
class StructureTestCase(unittest.TestCase):
|
||||
formats = {"c": c_char,
|
||||
"b": c_byte,
|
||||
"B": c_ubyte,
|
||||
"h": c_short,
|
||||
"H": c_ushort,
|
||||
"i": c_int,
|
||||
"I": c_uint,
|
||||
"l": c_long,
|
||||
"L": c_ulong,
|
||||
"q": c_longlong,
|
||||
"Q": c_ulonglong,
|
||||
"f": c_float,
|
||||
"d": c_double,
|
||||
}
|
||||
|
||||
def test_inheritance_hierarchy(self):
|
||||
self.assertEqual(Structure.mro(), [Structure, _CData, object])
|
||||
|
||||
self.assertEqual(PyCStructType.__name__, "PyCStructType")
|
||||
self.assertEqual(type(PyCStructType), type)
|
||||
|
||||
|
||||
def test_type_flags(self):
|
||||
for cls in Structure, PyCStructType:
|
||||
with self.subTest(cls=cls):
|
||||
self.assertTrue(Structure.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
|
||||
self.assertFalse(Structure.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
|
||||
|
||||
def test_metaclass_details(self):
|
||||
# Abstract classes (whose metaclass __init__ was not called) can't be
|
||||
# instantiated directly
|
||||
NewStructure = PyCStructType.__new__(PyCStructType, 'NewStructure',
|
||||
(Structure,), {})
|
||||
for cls in Structure, NewStructure:
|
||||
with self.subTest(cls=cls):
|
||||
with self.assertRaisesRegex(TypeError, "abstract class"):
|
||||
obj = cls()
|
||||
|
||||
# Cannot call the metaclass __init__ more than once
|
||||
class T(Structure):
|
||||
_fields_ = [("x", c_char),
|
||||
("y", c_char)]
|
||||
with self.assertRaisesRegex(SystemError, "already initialized"):
|
||||
PyCStructType.__init__(T, 'ptr', (), {})
|
||||
|
||||
def test_simple_structs(self):
|
||||
for code, tp in self.formats.items():
|
||||
class X(Structure):
|
||||
_fields_ = [("x", c_char),
|
||||
("y", tp)]
|
||||
self.assertEqual((sizeof(X), code),
|
||||
(calcsize("c%c0%c" % (code, code)), code))
|
||||
|
||||
def test_unions(self):
|
||||
for code, tp in self.formats.items():
|
||||
class X(Union):
|
||||
_fields_ = [("x", c_char),
|
||||
("y", tp)]
|
||||
self.assertEqual((sizeof(X), code),
|
||||
(calcsize("%c" % (code)), code))
|
||||
|
||||
def test_struct_alignment(self):
|
||||
class X(Structure):
|
||||
_fields_ = [("x", c_char * 3)]
|
||||
self.assertEqual(alignment(X), calcsize("s"))
|
||||
self.assertEqual(sizeof(X), calcsize("3s"))
|
||||
|
||||
class Y(Structure):
|
||||
_fields_ = [("x", c_char * 3),
|
||||
("y", c_int)]
|
||||
self.assertEqual(alignment(Y), alignment(c_int))
|
||||
self.assertEqual(sizeof(Y), calcsize("3si"))
|
||||
|
||||
class SI(Structure):
|
||||
_fields_ = [("a", X),
|
||||
("b", Y)]
|
||||
self.assertEqual(alignment(SI), max(alignment(Y), alignment(X)))
|
||||
self.assertEqual(sizeof(SI), calcsize("3s0i 3si 0i"))
|
||||
|
||||
class IS(Structure):
|
||||
_fields_ = [("b", Y),
|
||||
("a", X)]
|
||||
|
||||
self.assertEqual(alignment(SI), max(alignment(X), alignment(Y)))
|
||||
self.assertEqual(sizeof(IS), calcsize("3si 3s 0i"))
|
||||
|
||||
class XX(Structure):
|
||||
_fields_ = [("a", X),
|
||||
("b", X)]
|
||||
self.assertEqual(alignment(XX), alignment(X))
|
||||
self.assertEqual(sizeof(XX), calcsize("3s 3s 0s"))
|
||||
|
||||
def test_empty(self):
|
||||
# I had problems with these
|
||||
#
|
||||
# Although these are pathological cases: Empty Structures!
|
||||
class X(Structure):
|
||||
_fields_ = []
|
||||
|
||||
class Y(Union):
|
||||
_fields_ = []
|
||||
|
||||
# Is this really the correct alignment, or should it be 0?
|
||||
self.assertTrue(alignment(X) == alignment(Y) == 1)
|
||||
self.assertTrue(sizeof(X) == sizeof(Y) == 0)
|
||||
|
||||
class XX(Structure):
|
||||
_fields_ = [("a", X),
|
||||
("b", X)]
|
||||
|
||||
self.assertEqual(alignment(XX), 1)
|
||||
self.assertEqual(sizeof(XX), 0)
|
||||
|
||||
def test_fields(self):
|
||||
# test the offset and size attributes of Structure/Union fields.
|
||||
class X(Structure):
|
||||
_fields_ = [("x", c_int),
|
||||
("y", c_char)]
|
||||
|
||||
self.assertEqual(X.x.offset, 0)
|
||||
self.assertEqual(X.x.size, sizeof(c_int))
|
||||
|
||||
self.assertEqual(X.y.offset, sizeof(c_int))
|
||||
self.assertEqual(X.y.size, sizeof(c_char))
|
||||
|
||||
# readonly
|
||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "offset", 92)
|
||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "size", 92)
|
||||
|
||||
class X(Union):
|
||||
_fields_ = [("x", c_int),
|
||||
("y", c_char)]
|
||||
|
||||
self.assertEqual(X.x.offset, 0)
|
||||
self.assertEqual(X.x.size, sizeof(c_int))
|
||||
|
||||
self.assertEqual(X.y.offset, 0)
|
||||
self.assertEqual(X.y.size, sizeof(c_char))
|
||||
|
||||
# readonly
|
||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "offset", 92)
|
||||
self.assertRaises((TypeError, AttributeError), setattr, X.x, "size", 92)
|
||||
|
||||
# XXX Should we check nested data types also?
|
||||
# offset is always relative to the class...
|
||||
|
||||
def test_packed(self):
|
||||
class X(Structure):
|
||||
_fields_ = [("a", c_byte),
|
||||
|
@ -290,36 +105,6 @@ class StructureTestCase(unittest.TestCase):
|
|||
pt = POINT(y=2, x=1)
|
||||
self.assertEqual((pt.x, pt.y), (1, 2))
|
||||
|
||||
def test_invalid_field_types(self):
|
||||
class POINT(Structure):
|
||||
pass
|
||||
self.assertRaises(TypeError, setattr, POINT, "_fields_", [("x", 1), ("y", 2)])
|
||||
|
||||
def test_invalid_name(self):
|
||||
# field name must be string
|
||||
def declare_with_name(name):
|
||||
class S(Structure):
|
||||
_fields_ = [(name, c_int)]
|
||||
|
||||
self.assertRaises(TypeError, declare_with_name, b"x")
|
||||
|
||||
def test_intarray_fields(self):
|
||||
class SomeInts(Structure):
|
||||
_fields_ = [("a", c_int * 4)]
|
||||
|
||||
# can use tuple to initialize array (but not list!)
|
||||
self.assertEqual(SomeInts((1, 2)).a[:], [1, 2, 0, 0])
|
||||
self.assertEqual(SomeInts((1, 2)).a[::], [1, 2, 0, 0])
|
||||
self.assertEqual(SomeInts((1, 2)).a[::-1], [0, 0, 2, 1])
|
||||
self.assertEqual(SomeInts((1, 2)).a[::2], [1, 0])
|
||||
self.assertEqual(SomeInts((1, 2)).a[1:5:6], [2])
|
||||
self.assertEqual(SomeInts((1, 2)).a[6:4:-1], [])
|
||||
self.assertEqual(SomeInts((1, 2, 3, 4)).a[:], [1, 2, 3, 4])
|
||||
self.assertEqual(SomeInts((1, 2, 3, 4)).a[::], [1, 2, 3, 4])
|
||||
# too long
|
||||
# XXX Should raise ValueError?, not RuntimeError
|
||||
self.assertRaises(RuntimeError, SomeInts, (1, 2, 3, 4, 5))
|
||||
|
||||
def test_nested_initializers(self):
|
||||
# test initializing nested structures
|
||||
class Phone(Structure):
|
||||
|
@ -374,37 +159,12 @@ class StructureTestCase(unittest.TestCase):
|
|||
self.assertEqual(msg,
|
||||
"(Phone) TypeError: too many initializers")
|
||||
|
||||
def test_huge_field_name(self):
|
||||
# issue12881: segfault with large structure field names
|
||||
def create_class(length):
|
||||
class S(Structure):
|
||||
_fields_ = [('x' * length, c_int)]
|
||||
|
||||
for length in [10 ** i for i in range(0, 8)]:
|
||||
try:
|
||||
create_class(length)
|
||||
except MemoryError:
|
||||
# MemoryErrors are OK, we just don't want to segfault
|
||||
pass
|
||||
|
||||
def get_except(self, func, *args):
|
||||
try:
|
||||
func(*args)
|
||||
except Exception as detail:
|
||||
return detail.__class__, str(detail)
|
||||
|
||||
def test_abstract_class(self):
|
||||
class X(Structure):
|
||||
_abstract_ = "something"
|
||||
# try 'X()'
|
||||
cls, msg = self.get_except(eval, "X()", locals())
|
||||
self.assertEqual((cls, msg), (TypeError, "abstract class"))
|
||||
|
||||
def test_methods(self):
|
||||
self.assertIn("in_dll", dir(type(Structure)))
|
||||
self.assertIn("from_address", dir(type(Structure)))
|
||||
self.assertIn("in_dll", dir(type(Structure)))
|
||||
|
||||
def test_positional_args(self):
|
||||
# see also http://bugs.python.org/issue5042
|
||||
class W(Structure):
|
||||
|
@ -507,6 +267,8 @@ class StructureTestCase(unittest.TestCase):
|
|||
self.assertEqual(s.second, got.second)
|
||||
|
||||
def _test_issue18060(self, Vector):
|
||||
# Regression tests for gh-62260
|
||||
|
||||
# The call to atan2() should succeed if the
|
||||
# class fields were correctly cloned in the
|
||||
# subclasses. Otherwise, it will segfault.
|
||||
|
@ -698,6 +460,7 @@ class StructureTestCase(unittest.TestCase):
|
|||
self.assertEqual(result.data[i], float(i+1))
|
||||
|
||||
def test_38368(self):
|
||||
# Regression test for gh-82549
|
||||
class U(Union):
|
||||
_fields_ = [
|
||||
('f1', c_uint8 * 16),
|
||||
|
@ -719,9 +482,9 @@ class StructureTestCase(unittest.TestCase):
|
|||
self.assertEqual(f2, [0x4567, 0x0123, 0xcdef, 0x89ab,
|
||||
0x3210, 0x7654, 0xba98, 0xfedc])
|
||||
|
||||
@unittest.skipIf(True, 'Test disabled for now - see bpo-16575/bpo-16576')
|
||||
@unittest.skipIf(True, 'Test disabled for now - see gh-60779/gh-60780')
|
||||
def test_union_by_value(self):
|
||||
# See bpo-16575
|
||||
# See gh-60779
|
||||
|
||||
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
|
||||
|
||||
|
@ -800,9 +563,9 @@ class StructureTestCase(unittest.TestCase):
|
|||
self.assertEqual(test5.nested.an_int, 0)
|
||||
self.assertEqual(test5.another_int, 0)
|
||||
|
||||
@unittest.skipIf(True, 'Test disabled for now - see bpo-16575/bpo-16576')
|
||||
@unittest.skipIf(True, 'Test disabled for now - see gh-60779/gh-60780')
|
||||
def test_bitfield_by_value(self):
|
||||
# See bpo-16576
|
||||
# See gh-60780
|
||||
|
||||
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
|
||||
|
||||
|
@ -882,75 +645,5 @@ class StructureTestCase(unittest.TestCase):
|
|||
'a union by value, which is unsupported.')
|
||||
|
||||
|
||||
class PointerMemberTestCase(unittest.TestCase):
|
||||
|
||||
def test(self):
|
||||
# a Structure with a POINTER field
|
||||
class S(Structure):
|
||||
_fields_ = [("array", POINTER(c_int))]
|
||||
|
||||
s = S()
|
||||
# We can assign arrays of the correct type
|
||||
s.array = (c_int * 3)(1, 2, 3)
|
||||
items = [s.array[i] for i in range(3)]
|
||||
self.assertEqual(items, [1, 2, 3])
|
||||
|
||||
# The following are bugs, but are included here because the unittests
|
||||
# also describe the current behaviour.
|
||||
#
|
||||
# This fails with SystemError: bad arg to internal function
|
||||
# or with IndexError (with a patch I have)
|
||||
|
||||
s.array[0] = 42
|
||||
|
||||
items = [s.array[i] for i in range(3)]
|
||||
self.assertEqual(items, [42, 2, 3])
|
||||
|
||||
s.array[0] = 1
|
||||
|
||||
items = [s.array[i] for i in range(3)]
|
||||
self.assertEqual(items, [1, 2, 3])
|
||||
|
||||
def test_none_to_pointer_fields(self):
|
||||
class S(Structure):
|
||||
_fields_ = [("x", c_int),
|
||||
("p", POINTER(c_int))]
|
||||
|
||||
s = S()
|
||||
s.x = 12345678
|
||||
s.p = None
|
||||
self.assertEqual(s.x, 12345678)
|
||||
|
||||
|
||||
class TestRecursiveStructure(unittest.TestCase):
|
||||
def test_contains_itself(self):
|
||||
class Recursive(Structure):
|
||||
pass
|
||||
|
||||
try:
|
||||
Recursive._fields_ = [("next", Recursive)]
|
||||
except AttributeError as details:
|
||||
self.assertIn("Structure or union cannot contain itself",
|
||||
str(details))
|
||||
else:
|
||||
self.fail("Structure or union cannot contain itself")
|
||||
|
||||
|
||||
def test_vice_versa(self):
|
||||
class First(Structure):
|
||||
pass
|
||||
class Second(Structure):
|
||||
pass
|
||||
|
||||
First._fields_ = [("second", Second)]
|
||||
|
||||
try:
|
||||
Second._fields_ = [("first", First)]
|
||||
except AttributeError as details:
|
||||
self.assertIn("_fields_ is final", str(details))
|
||||
else:
|
||||
self.fail("AttributeError not raised")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import unittest
|
||||
from ctypes import Union, c_char
|
||||
from ._support import (_CData, UnionType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
||||
Py_TPFLAGS_IMMUTABLETYPE)
|
||||
|
||||
|
||||
class ArrayTestCase(unittest.TestCase):
|
||||
def test_inheritance_hierarchy(self):
|
||||
self.assertEqual(Union.mro(), [Union, _CData, object])
|
||||
|
||||
self.assertEqual(UnionType.__name__, "UnionType")
|
||||
self.assertEqual(type(UnionType), type)
|
||||
|
||||
def test_type_flags(self):
|
||||
for cls in Union, UnionType:
|
||||
with self.subTest(cls=Union):
|
||||
self.assertTrue(Union.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
|
||||
self.assertFalse(Union.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
|
||||
|
||||
def test_metaclass_details(self):
|
||||
# Abstract classes (whose metaclass __init__ was not called) can't be
|
||||
# instantiated directly
|
||||
NewUnion = UnionType.__new__(UnionType, 'NewUnion',
|
||||
(Union,), {})
|
||||
for cls in Union, NewUnion:
|
||||
with self.subTest(cls=cls):
|
||||
with self.assertRaisesRegex(TypeError, "abstract class"):
|
||||
obj = cls()
|
||||
|
||||
# Cannot call the metaclass __init__ more than once
|
||||
class T(Union):
|
||||
_fields_ = [("x", c_char),
|
||||
("y", c_char)]
|
||||
with self.assertRaisesRegex(SystemError, "already initialized"):
|
||||
UnionType.__init__(T, 'ptr', (), {})
|
Loading…
Add table
Add a link
Reference in a new issue