|
4 | 4 | from ctypes import (Structure, Union, POINTER, sizeof, alignment, |
5 | 5 | c_char, c_byte, c_ubyte, |
6 | 6 | c_short, c_ushort, c_int, c_uint, |
7 | | - c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double) |
| 7 | + c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double, |
| 8 | + c_int8, c_int16, c_int32) |
8 | 9 | from ._support import (_CData, PyCStructType, UnionType, |
9 | 10 | Py_TPFLAGS_DISALLOW_INSTANTIATION, |
10 | 11 | Py_TPFLAGS_IMMUTABLETYPE) |
@@ -175,6 +176,101 @@ class X(self.cls): |
175 | 176 | # XXX Should we check nested data types also? |
176 | 177 | # offset is always relative to the class... |
177 | 178 |
|
| 179 | + def test_field_descriptor_attributes(self): |
| 180 | + """Test information provided by the descriptors""" |
| 181 | + class Inner(Structure): |
| 182 | + _fields_ = [ |
| 183 | + ("a", c_int16), |
| 184 | + ("b", c_int8, 1), |
| 185 | + ("c", c_int8, 2), |
| 186 | + ] |
| 187 | + class X(self.cls): |
| 188 | + _fields_ = [ |
| 189 | + ("x", c_int32), |
| 190 | + ("y", c_int16, 1), |
| 191 | + ("_", Inner), |
| 192 | + ] |
| 193 | + _anonymous_ = ["_"] |
| 194 | + |
| 195 | + field_names = "xy_abc" |
| 196 | + |
| 197 | + # name |
| 198 | + |
| 199 | + for name in field_names: |
| 200 | + with self.subTest(name=name): |
| 201 | + self.assertEqual(getattr(X, name).name, name) |
| 202 | + |
| 203 | + # type |
| 204 | + |
| 205 | + expected_types = dict( |
| 206 | + x=c_int32, |
| 207 | + y=c_int16, |
| 208 | + _=Inner, |
| 209 | + a=c_int16, |
| 210 | + b=c_int8, |
| 211 | + c=c_int8, |
| 212 | + ) |
| 213 | + assert set(expected_types) == set(field_names) |
| 214 | + for name, tp in expected_types.items(): |
| 215 | + with self.subTest(name=name): |
| 216 | + self.assertEqual(getattr(X, name).type, tp) |
| 217 | + self.assertEqual(getattr(X, name).byte_size, sizeof(tp)) |
| 218 | + |
| 219 | + # offset, byte_offset |
| 220 | + |
| 221 | + expected_offsets = dict( |
| 222 | + x=(0, 0), |
| 223 | + y=(0, 4), |
| 224 | + _=(0, 6), |
| 225 | + a=(0, 6), |
| 226 | + b=(2, 8), |
| 227 | + c=(2, 8), |
| 228 | + ) |
| 229 | + assert set(expected_offsets) == set(field_names) |
| 230 | + for name, (union_offset, struct_offset) in expected_offsets.items(): |
| 231 | + with self.subTest(name=name): |
| 232 | + self.assertEqual(getattr(X, name).offset, |
| 233 | + getattr(X, name).byte_offset) |
| 234 | + if self.cls == Structure: |
| 235 | + self.assertEqual(getattr(X, name).offset, struct_offset) |
| 236 | + else: |
| 237 | + self.assertEqual(getattr(X, name).offset, union_offset) |
| 238 | + |
| 239 | + # is_bitfield, bit_size, bit_offset |
| 240 | + # size |
| 241 | + |
| 242 | + expected_bitfield_info = dict( |
| 243 | + # (bit_size, bit_offset) |
| 244 | + b=(1, 0), |
| 245 | + c=(2, 1), |
| 246 | + y=(1, 0), |
| 247 | + ) |
| 248 | + for name in field_names: |
| 249 | + with self.subTest(name=name): |
| 250 | + if info := expected_bitfield_info.get(name): |
| 251 | + self.assertEqual(getattr(X, name).is_bitfield, True) |
| 252 | + expected_bit_size, expected_bit_offset = info |
| 253 | + self.assertEqual(getattr(X, name).bit_size, |
| 254 | + expected_bit_size) |
| 255 | + self.assertEqual(getattr(X, name).bit_offset, |
| 256 | + expected_bit_offset) |
| 257 | + self.assertEqual(getattr(X, name).size, |
| 258 | + (expected_bit_size << 16) |
| 259 | + | expected_bit_offset) |
| 260 | + else: |
| 261 | + self.assertEqual(getattr(X, name).is_bitfield, False) |
| 262 | + type_size = sizeof(expected_types[name]) |
| 263 | + self.assertEqual(getattr(X, name).bit_size, type_size * 8) |
| 264 | + self.assertEqual(getattr(X, name).bit_offset, 0) |
| 265 | + self.assertEqual(getattr(X, name).size, type_size) |
| 266 | + |
| 267 | + # is_anonymous |
| 268 | + |
| 269 | + for name in field_names: |
| 270 | + with self.subTest(name=name): |
| 271 | + self.assertEqual(getattr(X, name).is_anonymous, (name == '_')) |
| 272 | + |
| 273 | + |
178 | 274 | def test_invalid_field_types(self): |
179 | 275 | class POINT(self.cls): |
180 | 276 | pass |
|
0 commit comments