1 import unittest 2 from ctypes import * 3 from struct import calcsize 4 import _testcapi 5 6 class SubclassesTest(unittest.TestCase): 7 def test_subclass(self): 8 class X(Structure): 9 _fields_ = [("a", c_int)] 10 11 class Y(X): 12 _fields_ = [("b", c_int)] 13 14 class Z(X): 15 pass 16 17 self.assertEqual(sizeof(X), sizeof(c_int)) 18 self.assertEqual(sizeof(Y), sizeof(c_int)*2) 19 self.assertEqual(sizeof(Z), sizeof(c_int)) 20 self.assertEqual(X._fields_, [("a", c_int)]) 21 self.assertEqual(Y._fields_, [("b", c_int)]) 22 self.assertEqual(Z._fields_, [("a", c_int)]) 23 24 def test_subclass_delayed(self): 25 class X(Structure): 26 pass 27 self.assertEqual(sizeof(X), 0) 28 X._fields_ = [("a", c_int)] 29 30 class Y(X): 31 pass 32 self.assertEqual(sizeof(Y), sizeof(X)) 33 Y._fields_ = [("b", c_int)] 34 35 class Z(X): 36 pass 37 38 self.assertEqual(sizeof(X), sizeof(c_int)) 39 self.assertEqual(sizeof(Y), sizeof(c_int)*2) 40 self.assertEqual(sizeof(Z), sizeof(c_int)) 41 self.assertEqual(X._fields_, [("a", c_int)]) 42 self.assertEqual(Y._fields_, [("b", c_int)]) 43 self.assertEqual(Z._fields_, [("a", c_int)]) 44 45 class StructureTestCase(unittest.TestCase): 46 formats = {"c": c_char, 47 "b": c_byte, 48 "B": c_ubyte, 49 "h": c_short, 50 "H": c_ushort, 51 "i": c_int, 52 "I": c_uint, 53 "l": c_long, 54 "L": c_ulong, 55 "q": c_longlong, 56 "Q": c_ulonglong, 57 "f": c_float, 58 "d": c_double, 59 } 60 61 def test_simple_structs(self): 62 for code, tp in self.formats.items(): 63 class X(Structure): 64 _fields_ = [("x", c_char), 65 ("y", tp)] 66 self.assertEqual((sizeof(X), code), 67 (calcsize("c%c0%c" % (code, code)), code)) 68 69 def test_unions(self): 70 for code, tp in self.formats.items(): 71 class X(Union): 72 _fields_ = [("x", c_char), 73 ("y", tp)] 74 self.assertEqual((sizeof(X), code), 75 (calcsize("%c" % (code)), code)) 76 77 def test_struct_alignment(self): 78 class X(Structure): 79 _fields_ = [("x", c_char * 3)] 80 self.assertEqual(alignment(X), calcsize("s")) 81 self.assertEqual(sizeof(X), calcsize("3s")) 82 83 class Y(Structure): 84 _fields_ = [("x", c_char * 3), 85 ("y", c_int)] 86 self.assertEqual(alignment(Y), calcsize("i")) 87 self.assertEqual(sizeof(Y), calcsize("3si")) 88 89 class SI(Structure): 90 _fields_ = [("a", X), 91 ("b", Y)] 92 self.assertEqual(alignment(SI), max(alignment(Y), alignment(X))) 93 self.assertEqual(sizeof(SI), calcsize("3s0i 3si 0i")) 94 95 class IS(Structure): 96 _fields_ = [("b", Y), 97 ("a", X)] 98 99 self.assertEqual(alignment(SI), max(alignment(X), alignment(Y))) 100 self.assertEqual(sizeof(IS), calcsize("3si 3s 0i")) 101 102 class XX(Structure): 103 _fields_ = [("a", X), 104 ("b", X)] 105 self.assertEqual(alignment(XX), alignment(X)) 106 self.assertEqual(sizeof(XX), calcsize("3s 3s 0s")) 107 108 def test_emtpy(self): 109 # I had problems with these 110 # 111 # Although these are patological cases: Empty Structures! 112 class X(Structure): 113 _fields_ = [] 114 115 class Y(Union): 116 _fields_ = [] 117 118 # Is this really the correct alignment, or should it be 0? 119 self.assertTrue(alignment(X) == alignment(Y) == 1) 120 self.assertTrue(sizeof(X) == sizeof(Y) == 0) 121 122 class XX(Structure): 123 _fields_ = [("a", X), 124 ("b", X)] 125 126 self.assertEqual(alignment(XX), 1) 127 self.assertEqual(sizeof(XX), 0) 128 129 def test_fields(self): 130 # test the offset and size attributes of Structure/Unoin fields. 131 class X(Structure): 132 _fields_ = [("x", c_int), 133 ("y", c_char)] 134 135 self.assertEqual(X.x.offset, 0) 136 self.assertEqual(X.x.size, sizeof(c_int)) 137 138 self.assertEqual(X.y.offset, sizeof(c_int)) 139 self.assertEqual(X.y.size, sizeof(c_char)) 140 141 # readonly 142 self.assertRaises((TypeError, AttributeError), setattr, X.x, "offset", 92) 143 self.assertRaises((TypeError, AttributeError), setattr, X.x, "size", 92) 144 145 class X(Union): 146 _fields_ = [("x", c_int), 147 ("y", c_char)] 148 149 self.assertEqual(X.x.offset, 0) 150 self.assertEqual(X.x.size, sizeof(c_int)) 151 152 self.assertEqual(X.y.offset, 0) 153 self.assertEqual(X.y.size, sizeof(c_char)) 154 155 # readonly 156 self.assertRaises((TypeError, AttributeError), setattr, X.x, "offset", 92) 157 self.assertRaises((TypeError, AttributeError), setattr, X.x, "size", 92) 158 159 # XXX Should we check nested data types also? 160 # offset is always relative to the class... 161 162 def test_packed(self): 163 class X(Structure): 164 _fields_ = [("a", c_byte), 165 ("b", c_longlong)] 166 _pack_ = 1 167 168 self.assertEqual(sizeof(X), 9) 169 self.assertEqual(X.b.offset, 1) 170 171 class X(Structure): 172 _fields_ = [("a", c_byte), 173 ("b", c_longlong)] 174 _pack_ = 2 175 self.assertEqual(sizeof(X), 10) 176 self.assertEqual(X.b.offset, 2) 177 178 class X(Structure): 179 _fields_ = [("a", c_byte), 180 ("b", c_longlong)] 181 _pack_ = 4 182 self.assertEqual(sizeof(X), 12) 183 self.assertEqual(X.b.offset, 4) 184 185 import struct 186 longlong_size = struct.calcsize("q") 187 longlong_align = struct.calcsize("bq") - longlong_size 188 189 class X(Structure): 190 _fields_ = [("a", c_byte), 191 ("b", c_longlong)] 192 _pack_ = 8 193 194 self.assertEqual(sizeof(X), longlong_align + longlong_size) 195 self.assertEqual(X.b.offset, min(8, longlong_align)) 196 197 198 d = {"_fields_": [("a", "b"), 199 ("b", "q")], 200 "_pack_": -1} 201 self.assertRaises(ValueError, type(Structure), "X", (Structure,), d) 202 203 # Issue 15989 204 d = {"_fields_": [("a", c_byte)], 205 "_pack_": _testcapi.INT_MAX + 1} 206 self.assertRaises(ValueError, type(Structure), "X", (Structure,), d) 207 d = {"_fields_": [("a", c_byte)], 208 "_pack_": _testcapi.UINT_MAX + 2} 209 self.assertRaises(ValueError, type(Structure), "X", (Structure,), d) 210 211 def test_initializers(self): 212 class Person(Structure): 213 _fields_ = [("name", c_char*6), 214 ("age", c_int)] 215 216 self.assertRaises(TypeError, Person, 42) 217 self.assertRaises(ValueError, Person, "asldkjaslkdjaslkdj") 218 self.assertRaises(TypeError, Person, "Name", "HI") 219 220 # short enough 221 self.assertEqual(Person("12345", 5).name, "12345") 222 # exact fit 223 self.assertEqual(Person("123456", 5).name, "123456") 224 # too long 225 self.assertRaises(ValueError, Person, "1234567", 5) 226 227 def test_conflicting_initializers(self): 228 class POINT(Structure): 229 _fields_ = [("x", c_int), ("y", c_int)] 230 # conflicting positional and keyword args 231 self.assertRaises(TypeError, POINT, 2, 3, x=4) 232 self.assertRaises(TypeError, POINT, 2, 3, y=4) 233 234 # too many initializers 235 self.assertRaises(TypeError, POINT, 2, 3, 4) 236 237 def test_keyword_initializers(self): 238 class POINT(Structure): 239 _fields_ = [("x", c_int), ("y", c_int)] 240 pt = POINT(1, 2) 241 self.assertEqual((pt.x, pt.y), (1, 2)) 242 243 pt = POINT(y=2, x=1) 244 self.assertEqual((pt.x, pt.y), (1, 2)) 245 246 def test_invalid_field_types(self): 247 class POINT(Structure): 248 pass 249 self.assertRaises(TypeError, setattr, POINT, "_fields_", [("x", 1), ("y", 2)]) 250 251 def test_invalid_name(self): 252 # field name must be string 253 def declare_with_name(name): 254 class S(Structure): 255 _fields_ = [(name, c_int)] 256 257 self.assertRaises(TypeError, declare_with_name, u"x\xe9") 258 259 def test_intarray_fields(self): 260 class SomeInts(Structure): 261 _fields_ = [("a", c_int * 4)] 262 263 # can use tuple to initialize array (but not list!) 264 self.assertEqual(SomeInts((1, 2)).a[:], [1, 2, 0, 0]) 265 self.assertEqual(SomeInts((1, 2)).a[::], [1, 2, 0, 0]) 266 self.assertEqual(SomeInts((1, 2)).a[::-1], [0, 0, 2, 1]) 267 self.assertEqual(SomeInts((1, 2)).a[::2], [1, 0]) 268 self.assertEqual(SomeInts((1, 2)).a[1:5:6], [2]) 269 self.assertEqual(SomeInts((1, 2)).a[6:4:-1], []) 270 self.assertEqual(SomeInts((1, 2, 3, 4)).a[:], [1, 2, 3, 4]) 271 self.assertEqual(SomeInts((1, 2, 3, 4)).a[::], [1, 2, 3, 4]) 272 # too long 273 # XXX Should raise ValueError?, not RuntimeError 274 self.assertRaises(RuntimeError, SomeInts, (1, 2, 3, 4, 5)) 275 276 def test_nested_initializers(self): 277 # test initializing nested structures 278 class Phone(Structure): 279 _fields_ = [("areacode", c_char*6), 280 ("number", c_char*12)] 281 282 class Person(Structure): 283 _fields_ = [("name", c_char * 12), 284 ("phone", Phone), 285 ("age", c_int)] 286 287 p = Person("Someone", ("1234", "5678"), 5) 288 289 self.assertEqual(p.name, "Someone") 290 self.assertEqual(p.phone.areacode, "1234") 291 self.assertEqual(p.phone.number, "5678") 292 self.assertEqual(p.age, 5) 293 294 def test_structures_with_wchar(self): 295 try: 296 c_wchar 297 except NameError: 298 return # no unicode 299 300 class PersonW(Structure): 301 _fields_ = [("name", c_wchar * 12), 302 ("age", c_int)] 303 304 p = PersonW(u"Someone") 305 self.assertEqual(p.name, "Someone") 306 307 self.assertEqual(PersonW(u"1234567890").name, u"1234567890") 308 self.assertEqual(PersonW(u"12345678901").name, u"12345678901") 309 # exact fit 310 self.assertEqual(PersonW(u"123456789012").name, u"123456789012") 311 #too long 312 self.assertRaises(ValueError, PersonW, u"1234567890123") 313 314 def test_init_errors(self): 315 class Phone(Structure): 316 _fields_ = [("areacode", c_char*6), 317 ("number", c_char*12)] 318 319 class Person(Structure): 320 _fields_ = [("name", c_char * 12), 321 ("phone", Phone), 322 ("age", c_int)] 323 324 cls, msg = self.get_except(Person, "Someone", (1, 2)) 325 self.assertEqual(cls, RuntimeError) 326 # In Python 2.5, Exception is a new-style class, and the repr changed 327 if issubclass(Exception, object): 328 self.assertEqual(msg, 329 "(Phone) <type 'exceptions.TypeError'>: " 330 "expected string or Unicode object, int found") 331 else: 332 self.assertEqual(msg, 333 "(Phone) exceptions.TypeError: " 334 "expected string or Unicode object, int found") 335 336 cls, msg = self.get_except(Person, "Someone", ("a", "b", "c")) 337 self.assertEqual(cls, RuntimeError) 338 if issubclass(Exception, object): 339 self.assertEqual(msg, 340 "(Phone) <type 'exceptions.TypeError'>: too many initializers") 341 else: 342 self.assertEqual(msg, "(Phone) exceptions.TypeError: too many initializers") 343 344 def test_huge_field_name(self): 345 # issue12881: segfault with large structure field names 346 def create_class(length): 347 class S(Structure): 348 _fields_ = [('x' * length, c_int)] 349 350 for length in [10 ** i for i in range(0, 8)]: 351 try: 352 create_class(length) 353 except MemoryError: 354 # MemoryErrors are OK, we just don't want to segfault 355 pass 356 357 def get_except(self, func, *args): 358 try: 359 func(*args) 360 except Exception, detail: 361 return detail.__class__, str(detail) 362 363 364 ## def test_subclass_creation(self): 365 ## meta = type(Structure) 366 ## # same as 'class X(Structure): pass' 367 ## # fails, since we need either a _fields_ or a _abstract_ attribute 368 ## cls, msg = self.get_except(meta, "X", (Structure,), {}) 369 ## self.assertEqual((cls, msg), 370 ## (AttributeError, "class must define a '_fields_' attribute")) 371 372 def test_abstract_class(self): 373 class X(Structure): 374 _abstract_ = "something" 375 # try 'X()' 376 cls, msg = self.get_except(eval, "X()", locals()) 377 self.assertEqual((cls, msg), (TypeError, "abstract class")) 378 379 def test_methods(self): 380 ## class X(Structure): 381 ## _fields_ = [] 382 383 self.assertTrue("in_dll" in dir(type(Structure))) 384 self.assertTrue("from_address" in dir(type(Structure))) 385 self.assertTrue("in_dll" in dir(type(Structure))) 386 387 def test_positional_args(self): 388 # see also http://bugs.python.org/issue5042 389 class W(Structure): 390 _fields_ = [("a", c_int), ("b", c_int)] 391 class X(W): 392 _fields_ = [("c", c_int)] 393 class Y(X): 394 pass 395 class Z(Y): 396 _fields_ = [("d", c_int), ("e", c_int), ("f", c_int)] 397 398 z = Z(1, 2, 3, 4, 5, 6) 399 self.assertEqual((z.a, z.b, z.c, z.d, z.e, z.f), 400 (1, 2, 3, 4, 5, 6)) 401 z = Z(1) 402 self.assertEqual((z.a, z.b, z.c, z.d, z.e, z.f), 403 (1, 0, 0, 0, 0, 0)) 404 self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7)) 405 406 class PointerMemberTestCase(unittest.TestCase): 407 408 def test(self): 409 # a Structure with a POINTER field 410 class S(Structure): 411 _fields_ = [("array", POINTER(c_int))] 412 413 s = S() 414 # We can assign arrays of the correct type 415 s.array = (c_int * 3)(1, 2, 3) 416 items = [s.array[i] for i in range(3)] 417 self.assertEqual(items, [1, 2, 3]) 418 419 # The following are bugs, but are included here because the unittests 420 # also describe the current behaviour. 421 # 422 # This fails with SystemError: bad arg to internal function 423 # or with IndexError (with a patch I have) 424 425 s.array[0] = 42 426 427 items = [s.array[i] for i in range(3)] 428 self.assertEqual(items, [42, 2, 3]) 429 430 s.array[0] = 1 431 432 ## s.array[1] = 42 433 434 items = [s.array[i] for i in range(3)] 435 self.assertEqual(items, [1, 2, 3]) 436 437 def test_none_to_pointer_fields(self): 438 class S(Structure): 439 _fields_ = [("x", c_int), 440 ("p", POINTER(c_int))] 441 442 s = S() 443 s.x = 12345678 444 s.p = None 445 self.assertEqual(s.x, 12345678) 446 447 class TestRecursiveStructure(unittest.TestCase): 448 def test_contains_itself(self): 449 class Recursive(Structure): 450 pass 451 452 try: 453 Recursive._fields_ = [("next", Recursive)] 454 except AttributeError, details: 455 self.assertTrue("Structure or union cannot contain itself" in 456 str(details)) 457 else: 458 self.fail("Structure or union cannot contain itself") 459 460 461 def test_vice_versa(self): 462 class First(Structure): 463 pass 464 class Second(Structure): 465 pass 466 467 First._fields_ = [("second", Second)] 468 469 try: 470 Second._fields_ = [("first", First)] 471 except AttributeError, details: 472 self.assertTrue("_fields_ is final" in 473 str(details)) 474 else: 475 self.fail("AttributeError not raised") 476 477 if __name__ == '__main__': 478 unittest.main() 479