this repo has no description
at trunk 311 lines 11 kB view raw
1#!/usr/bin/env python3 2# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 3import ctypes 4import ctypes.util 5import unittest 6 7from test_support import pyro_only 8 9 10class CtypesTests(unittest.TestCase): 11 def test_addressof_non_cdata_raises_type_error(self): 12 with self.assertRaises(TypeError) as ctx: 13 ctypes.addressof("not-CData") 14 self.assertEqual(str(ctx.exception), "invalid type") 15 16 def test_addressof_mmap_returns_int_greater_than_zero(self): 17 import mmap 18 19 view = memoryview(mmap.mmap(-1, 2)) 20 uint = ctypes.c_uint16.from_buffer(view) 21 addr = ctypes.addressof(uint) 22 self.assertIsInstance(addr, int) 23 self.assertGreater(addr, 0) 24 25 def test_array_subclass_is_instantiable(self): 26 class ArraySub(ctypes.Array): 27 _length_ = 2 28 _type_ = ctypes.c_bool 29 30 self.assertEqual(ctypes.sizeof(ArraySub()), 2) 31 32 def test_array_without_length_raises_attribute_error(self): 33 with self.assertRaises(AttributeError) as ctx: 34 35 class ArraySub(ctypes.Array): 36 _type_ = ctypes.c_bool 37 38 self.assertEqual(str(ctx.exception), "class must define a '_length_' attribute") 39 40 def test_array_with_non_int_length_raises_type_error(self): 41 with self.assertRaises(TypeError) as ctx: 42 43 class ArraySub(ctypes.Array): 44 _length_ = 1.0 45 _type_ = ctypes.c_bool 46 47 self.assertEqual( 48 str(ctx.exception), "The '_length_' attribute must be an integer" 49 ) 50 51 def test_array_with_negative_length_raises_attribute_error(self): 52 with self.assertRaises(ValueError) as ctx: 53 54 class ArraySub(ctypes.Array): 55 _length_ = -1 56 _type_ = ctypes.c_bool 57 58 self.assertEqual( 59 str(ctx.exception), "The '_length_' attribute must not be negative" 60 ) 61 62 def test_array_without_type_raises_attribute_error(self): 63 with self.assertRaises(AttributeError) as ctx: 64 65 class ArraySub(ctypes.Array): 66 _length_ = 1 67 68 self.assertEqual(str(ctx.exception), "class must define a '_type_' attribute") 69 70 def test_array_with_non_class_type_raises_type_error(self): 71 with self.assertRaises(TypeError) as ctx: 72 73 class ArraySub(ctypes.Array): 74 _length_ = 1 75 _type_ = "b" 76 77 self.assertEqual(str(ctx.exception), "_type_ must have storage info") 78 79 def test_array_with_non_CData_type_raises_type_error(self): 80 with self.assertRaises(TypeError) as ctx: 81 82 class ArraySub(ctypes.Array): 83 _length_ = 1 84 _type_ = int 85 86 self.assertEqual(str(ctx.exception), "_type_ must have storage info") 87 88 def test_cfuncptr_dunder_call_no_args_c_int_restype_returns_int(self): 89 lib_name = ctypes.util.find_library("python") 90 lib = ctypes.CDLL(lib_name) 91 recursion_limit = lib.Py_GetRecursionLimit 92 self.assertIs(recursion_limit.restype, ctypes.c_int) 93 result = recursion_limit() 94 self.assertIs(type(result), int) 95 self.assertEqual(result, 1000) 96 97 def test_cfuncptr_dunder_call_no_args_c_char_p_restype_returns_bytes(self): 98 lib_name = ctypes.util.find_library("python") 99 lib = ctypes.CDLL(lib_name) 100 get_platform = lib.Py_GetPlatform 101 self.assertIs(get_platform.restype, ctypes.c_int) 102 get_platform.restype = ctypes.c_char_p 103 result = get_platform() 104 self.assertIs(type(result), bytes) 105 self.assertTrue(result in [b"linux", b"darwin"]) 106 107 def test_char_array_from_buffer_is_readable_and_writeable(self): 108 import mmap 109 110 m = mmap.mmap(-1, 6) 111 array_type = ctypes.c_char * 6 112 c = array_type.from_buffer(memoryview(m)) 113 view = memoryview(m) 114 view[0] = ord("f") 115 view[1] = ord("b") 116 self.assertEqual(c.value, b"fb") 117 c.value = b"foobar" 118 self.assertEqual(c.value, b"foobar") 119 self.assertEqual(view.tolist(), list(b"foobar")) 120 121 def test_char_array_with_zero_initialization_rewrites_first_byte_memory(self): 122 import mmap 123 124 m = mmap.mmap(-1, 3) 125 array_type = ctypes.c_char * 3 126 view = memoryview(m) 127 view[:3] = b"foo" 128 c = array_type.from_buffer(memoryview(m)) 129 c.__init__(b"\0") 130 self.assertEqual(c.value, b"") 131 self.assertEqual(view.tolist(), list(b"\0oo")) 132 133 def test_char_array_with_non_bytes_value_raises_type_error(self): 134 c = (ctypes.c_char * 3)() 135 with self.assertRaises(TypeError) as ctx: 136 c.value = 1 137 self.assertEqual(str(ctx.exception), "bytes expected instead of int instance") 138 139 def test_char_array_with_too_long_value_raises_value_error(self): 140 c = (ctypes.c_char * 3)() 141 with self.assertRaises(ValueError) as ctx: 142 c.value = b"foobar" 143 self.assertEqual(str(ctx.exception), "byte string too long") 144 145 def test_non_char_array_class_raises_attribute_error_on_value(self): 146 array_type = ctypes.c_uint16 * 5 147 with self.assertRaises(AttributeError) as ctx: 148 array_type().value 149 self.assertEqual( 150 str(ctx.exception), "'c_ushort_Array_5' object has no attribute 'value'" 151 ) 152 153 def test_ctypes_array_creation_with_type_returns_array_type(self): 154 arr = ctypes.c_ubyte * 14 155 self.assertEqual(arr._type_, ctypes.c_ubyte) 156 self.assertEqual(arr._length_, 14) 157 self.assertEqual(arr.__name__, "c_ubyte_Array_14") 158 159 def test_ctypes_array_creation_cache_returns_same_instance_for_same_type(self): 160 arr1 = ctypes.c_ubyte * 2 161 arr2 = ctypes.c_ubyte * 2 162 self.assertIs(arr1, arr2) 163 164 # TODO(T47024191): Since inline caches hold strongrefs to their objects, array 165 # types don't get collected on _gc(). 166 @unittest.skip("Disable until our inline caches store weakrefs") 167 @pyro_only 168 def test_ctypes_array_creation_cache_cleared_when_type_gced(self): 169 from _ctypes import _array_from_ctype_cache 170 171 from _builtins import _gc 172 173 ctypes.c_ubyte * 2 174 self.assertGreater(len(_array_from_ctype_cache), 0) 175 _gc() 176 self.assertEqual(len(_array_from_ctype_cache), 0) 177 178 def test_ctypes_uint16_creation_stores_uint16(self): 179 self.assertEqual(ctypes.c_uint16().value, 0) 180 self.assertEqual(ctypes.c_uint16(10).value, 10) 181 self.assertEqual(ctypes.c_uint16(True).value, 1) 182 183 def test_ctypes_uint16_value_is_modifiable(self): 184 uint = ctypes.c_uint16() 185 self.assertEqual(uint.value, 0) 186 uint.value = 12345 187 self.assertEqual(uint.value, 12345) 188 with self.assertRaises(TypeError) as ctx: 189 uint.value = 1.0 190 self.assertEqual(str(ctx.exception), "int expected instead of float") 191 192 def test_ctypes_uint16_with_wrong_type_raises_type_error(self): 193 with self.assertRaises(TypeError) as ctx: 194 ctypes.c_uint16("1") 195 self.assertEqual(str(ctx.exception), "an integer is required (got type str)") 196 197 def test_ctypes_uint16_with_float_raises_type_error(self): 198 with self.assertRaises(TypeError) as ctx: 199 ctypes.c_uint16(1.0) 200 self.assertEqual(str(ctx.exception), "int expected instead of float") 201 202 def test_ctypes_ulonglong_creation_stores_long(self): 203 self.assertEqual(ctypes.c_ulonglong().value, 0) 204 self.assertEqual(ctypes.c_ulonglong(0xFF << 32).value, 0xFF << 32) 205 206 def test_from_buffer_with_memory_reflects_memory(self): 207 import mmap 208 209 m = mmap.mmap(-1, 2) 210 c = ctypes.c_uint16.from_buffer(memoryview(m)) 211 view = memoryview(m) 212 view[0] = ord("f") 213 view[1] = ord("b") 214 self.assertEqual(c.value, (view[1] << 8) + view[0]) 215 216 def test_from_buffer_with_abstract_class_raises_type_error(self): 217 import _ctypes 218 219 with self.assertRaises(TypeError) as context: 220 _ctypes._SimpleCData.from_buffer(b"as") 221 self.assertEqual(str(context.exception), "abstract class") 222 223 def test_from_buffer_with_readonly_buffer_raises_type_error(self): 224 import mmap 225 226 m = mmap.mmap(-1, 2, prot=mmap.PROT_READ) 227 with self.assertRaises(TypeError) as context: 228 ctypes.c_uint16.from_buffer(memoryview(m)) 229 self.assertEqual(str(context.exception), "underlying buffer is not writable") 230 231 def test_from_buffer_with_negative_offset_raises_value_error(self): 232 import mmap 233 234 m = mmap.mmap(-1, 2) 235 with self.assertRaises(ValueError) as context: 236 ctypes.c_uint16.from_buffer(memoryview(m), -1) 237 self.assertEqual(str(context.exception), "offset cannot be negative") 238 239 def test_from_buffer_with_too_small_buffer_raises_value_error(self): 240 import mmap 241 242 m = mmap.mmap(-1, 1) 243 with self.assertRaises(ValueError) as context: 244 ctypes.c_uint16.from_buffer(memoryview(m)) 245 self.assertEqual( 246 str(context.exception), 247 "Buffer size too small (1 instead of at least 2 bytes)", 248 ) 249 250 def test_from_buffer_with_different_sizes_read_correct_number_of_bytes(self): 251 import mmap 252 253 m = mmap.mmap(-1, 8) 254 view = memoryview(m) 255 view[0] = 0xFF 256 view[3] = 0xFF 257 short = ctypes.c_uint16.from_buffer(view) 258 longlong = ctypes.c_ulonglong.from_buffer(view) 259 self.assertEqual(short.value, 0xFF) 260 self.assertEqual(longlong.value, (0xFF << 24) + 0xFF) 261 262 def test_loaded_library_has_function_attrs(self): 263 libc_name = ctypes.util.find_library("c") 264 libc = ctypes.CDLL(libc_name) 265 self.assertTrue(hasattr(libc, "abort")) 266 self.assertTrue(hasattr(libc, "printf")) 267 self.assertFalse(hasattr(libc, "DoesNotExist")) 268 self.assertIsNotNone(libc.abort) 269 self.assertIsNotNone(libc.printf) 270 271 def test_import_uuid(self): 272 # importing uuid exercises ctypes, make sure it doesn't break 273 import uuid 274 275 self.assertIsNotNone(uuid) 276 277 def test_memset_sets_memoryview_array(self): 278 import mmap 279 280 m = mmap.mmap(-1, 4) 281 view = memoryview(m) 282 view[:] = b"1234" 283 array = (ctypes.c_char * 4).from_buffer(view) 284 array_addr = ctypes.addressof(array) 285 self.assertEqual(array_addr, ctypes.memset(array_addr, 1, 2)) 286 self.assertEqual(view.tolist(), list(b"\x01\x0134")) 287 288 def test_sizeof_non_cdata_type_raises_type_error(self): 289 with self.assertRaises(TypeError) as ctx: 290 ctypes.sizeof(TypeError) 291 self.assertEqual(str(ctx.exception), "this type has no size") 292 293 def test_sizeof_type_returns_size(self): 294 self.assertEqual(ctypes.sizeof(ctypes.c_bool), 1) 295 self.assertEqual(ctypes.sizeof(ctypes.c_uint16), 2) 296 297 def test_sizeof_object_with_memory_returns_size_of_type(self): 298 import mmap 299 300 view = memoryview(mmap.mmap(-1, 6)) 301 uint = ctypes.c_uint16.from_buffer(view) 302 self.assertEqual(ctypes.sizeof(uint), 2) 303 304 def test_sizeof_array_returns_length(self): 305 self.assertEqual(ctypes.sizeof(ctypes.c_char * 5), 5) 306 self.assertEqual(ctypes.sizeof(ctypes.c_uint16 * 20), 40) 307 self.assertEqual(ctypes.sizeof(ctypes.c_uint16 * 5 * 5), 50) 308 309 310if __name__ == "__main__": 311 unittest.main()