|
| 1 | +# Test PyMarshal C API |
| 2 | + |
| 3 | +import marshal |
| 4 | +import struct |
| 5 | +import unittest |
| 6 | +from test.support import import_helper |
| 7 | +from test.support import os_helper |
| 8 | + |
| 9 | +_testcapi = import_helper.import_module('_testcapi') |
| 10 | + |
| 11 | +NULL = None |
| 12 | +Py_MARSHAL_VERSION = _testcapi.Py_MARSHAL_VERSION |
| 13 | + |
| 14 | +def noop_func(): |
| 15 | + pass |
| 16 | + |
| 17 | +SIMPLE_OBJECT = 123 |
| 18 | +# Only test a few objects: see test_marshal for more exhaustive tests |
| 19 | +TEST_OBJECTS = ( |
| 20 | + '\u20ac', |
| 21 | + b'abc', |
| 22 | + True, |
| 23 | + 45.6, |
| 24 | + 7+8j, |
| 25 | + SIMPLE_OBJECT, |
| 26 | + # Check that serializing code object is allowed (allow_code = 1) |
| 27 | + noop_func.__code__, |
| 28 | +) |
| 29 | + |
| 30 | +# Invalid marshal data |
| 31 | +JUNK_BYTES = b'\xff' * 32 |
| 32 | + |
| 33 | + |
| 34 | +def read_file(filename): |
| 35 | + with open(filename, 'rb') as fp: |
| 36 | + return fp.read() |
| 37 | + |
| 38 | + |
| 39 | +def write_file(filename, data): |
| 40 | + with open(filename, 'wb') as fp: |
| 41 | + fp.write(data) |
| 42 | + |
| 43 | + |
| 44 | +class CAPIUnicodeTest(unittest.TestCase): |
| 45 | + def check_object(self, obj2, obj): |
| 46 | + self.assertEqual(obj2, obj) |
| 47 | + self.assertEqual(type(obj2), type(obj)) |
| 48 | + |
| 49 | + def test_pymarshal_readobjectfromstring(self): |
| 50 | + # Test PyMarshal_ReadObjectFromString() |
| 51 | + readobjectfromstring = _testcapi.pymarshal_readobjectfromstring |
| 52 | + for obj in TEST_OBJECTS: |
| 53 | + for version in range(Py_MARSHAL_VERSION + 1): |
| 54 | + with self.subTest(obj=obj, version=version): |
| 55 | + data = marshal.dumps(obj, version) |
| 56 | + obj2 = readobjectfromstring(data) |
| 57 | + self.check_object(obj2, obj) |
| 58 | + |
| 59 | + data = marshal.dumps(SIMPLE_OBJECT, Py_MARSHAL_VERSION) |
| 60 | + data = data[:-1] # truncate |
| 61 | + with self.assertRaises(EOFError): |
| 62 | + readobjectfromstring(data) |
| 63 | + |
| 64 | + with self.assertRaisesRegex(ValueError, 'bad marshal data'): |
| 65 | + readobjectfromstring(JUNK_BYTES) |
| 66 | + |
| 67 | + def test_pymarshal_writeobjecttostring(self): |
| 68 | + # Test PyMarshal_WriteObjectToString() |
| 69 | + writeobjecttostring = _testcapi.pymarshal_writeobjecttostring |
| 70 | + for version in range(Py_MARSHAL_VERSION + 1): |
| 71 | + for obj in TEST_OBJECTS: |
| 72 | + with self.subTest(obj=obj, version=version): |
| 73 | + data = writeobjecttostring(obj, version) |
| 74 | + obj2 = marshal.loads(data) |
| 75 | + self.check_object(obj2, obj) |
| 76 | + |
| 77 | + with self.assertRaises(SystemError): |
| 78 | + writeobjecttostring(NULL, version) |
| 79 | + |
| 80 | + def test_pymarshal_writeobjecttofile(self): |
| 81 | + # Test PyMarshal_WriteObjectToFile() |
| 82 | + writeobjecttofile = _testcapi.pymarshal_writeobjecttofile |
| 83 | + |
| 84 | + filename = os_helper.TESTFN |
| 85 | + self.addCleanup(os_helper.unlink, filename) |
| 86 | + |
| 87 | + for version in range(Py_MARSHAL_VERSION + 1): |
| 88 | + for obj in TEST_OBJECTS: |
| 89 | + with self.subTest(obj=obj, version=version): |
| 90 | + writeobjecttofile(obj, filename, version) |
| 91 | + data = read_file(filename) |
| 92 | + obj2 = marshal.loads(data) |
| 93 | + self.check_object(obj2, obj) |
| 94 | + |
| 95 | + with self.assertRaises(SystemError): |
| 96 | + writeobjecttofile(NULL, filename, version) |
| 97 | + |
| 98 | + def test_pymarshal_writelongtofile(self): |
| 99 | + # Test PyMarshal_WriteLongToFile() |
| 100 | + writelongtofile = _testcapi.pymarshal_writelongtofile |
| 101 | + |
| 102 | + def mask32(value): |
| 103 | + res = value & (2 ** 32 - 1) |
| 104 | + if res >= 2147483648: |
| 105 | + return res - 4294967296 |
| 106 | + else: |
| 107 | + return res |
| 108 | + |
| 109 | + filename = os_helper.TESTFN |
| 110 | + self.addCleanup(os_helper.unlink, filename) |
| 111 | + |
| 112 | + limit = 2 ** 31 |
| 113 | + for version in range(Py_MARSHAL_VERSION + 1): |
| 114 | + for value in ( |
| 115 | + _testcapi.LONG_MIN, |
| 116 | + _testcapi.LONG_MAX, |
| 117 | + -limit - 2, |
| 118 | + -limit, |
| 119 | + -limit + 2, |
| 120 | + limit - 2, |
| 121 | + limit, |
| 122 | + limit + 2, |
| 123 | + 0, |
| 124 | + 123, |
| 125 | + -123, |
| 126 | + ): |
| 127 | + with self.subTest(value=value, version=version): |
| 128 | + writelongtofile(value, filename, version) |
| 129 | + data = read_file(filename) |
| 130 | + self.assertEqual(len(data), 4) |
| 131 | + value2 = struct.unpack('<i', data)[0] |
| 132 | + self.assertEqual(value2, mask32(value)) |
| 133 | + |
| 134 | + def test_pymarshal_readshortfromfile(self): |
| 135 | + # Test PyMarshal_ReadShortFromFile() |
| 136 | + readshortfromfile = _testcapi.pymarshal_readshortfromfile |
| 137 | + |
| 138 | + filename = os_helper.TESTFN |
| 139 | + self.addCleanup(os_helper.unlink, filename) |
| 140 | + |
| 141 | + for value in ( |
| 142 | + -2**15, |
| 143 | + 2**15-1, |
| 144 | + 0, |
| 145 | + 123, |
| 146 | + -123, |
| 147 | + ): |
| 148 | + with self.subTest(value=value): |
| 149 | + data = struct.pack('<h', value) |
| 150 | + write_file(filename, data) |
| 151 | + value2 = readshortfromfile(filename) |
| 152 | + self.assertEqual(value2, value) |
| 153 | + |
| 154 | + write_file(filename, b'\x00') # less than 2 bytes |
| 155 | + with self.assertRaises(EOFError): |
| 156 | + readshortfromfile(filename) |
| 157 | + |
| 158 | + def test_pymarshal_readlongfromfile(self): |
| 159 | + # Test PyMarshal_ReadLongFromFile() |
| 160 | + readlongfromfile = _testcapi.pymarshal_readlongfromfile |
| 161 | + |
| 162 | + filename = os_helper.TESTFN |
| 163 | + self.addCleanup(os_helper.unlink, filename) |
| 164 | + |
| 165 | + for value in ( |
| 166 | + -2**31, |
| 167 | + 2**31-1, |
| 168 | + 0, |
| 169 | + 123, |
| 170 | + -123, |
| 171 | + ): |
| 172 | + with self.subTest(value=value): |
| 173 | + data = struct.pack('<i', value) |
| 174 | + write_file(filename, data) |
| 175 | + value2 = readlongfromfile(filename) |
| 176 | + self.assertEqual(value2, value) |
| 177 | + |
| 178 | + write_file(filename, b'\x00\x01\x02') # less than 4 bytes |
| 179 | + with self.assertRaises(EOFError): |
| 180 | + readlongfromfile(filename) |
| 181 | + |
| 182 | + def check_read_object(self, read_object_func): |
| 183 | + filename = os_helper.TESTFN |
| 184 | + self.addCleanup(os_helper.unlink, filename) |
| 185 | + |
| 186 | + version = Py_MARSHAL_VERSION |
| 187 | + for obj in TEST_OBJECTS: |
| 188 | + with self.subTest(obj=obj): |
| 189 | + data = marshal.dumps(obj, version) |
| 190 | + data += b'abc' # following data is ignored |
| 191 | + write_file(filename, data) |
| 192 | + obj2 = read_object_func(filename) |
| 193 | + self.check_object(obj2, obj) |
| 194 | + |
| 195 | + data = marshal.dumps(SIMPLE_OBJECT, version) |
| 196 | + data = data[:-1] # truncate |
| 197 | + write_file(filename, data) |
| 198 | + with self.assertRaises(EOFError): |
| 199 | + read_object_func(filename) |
| 200 | + |
| 201 | + write_file(filename, JUNK_BYTES) |
| 202 | + with self.assertRaisesRegex(ValueError, 'bad marshal data'): |
| 203 | + read_object_func(filename) |
| 204 | + |
| 205 | + def test_pymarshal_readobjetfromfile(self): |
| 206 | + # Test PyMarshal_ReadObjectFromFile() |
| 207 | + readobjectfromfile = _testcapi.pymarshal_readobjectfromfile |
| 208 | + self.check_read_object(readobjectfromfile) |
| 209 | + |
| 210 | + def test_pymarshal_readlastobjetfromfile(self): |
| 211 | + # Test PyMarshal_ReadLastObjectFromFile() |
| 212 | + readlastobjectfromfile = _testcapi.pymarshal_readlastobjectfromfile |
| 213 | + self.check_read_object(readlastobjectfromfile) |
| 214 | + |
| 215 | + |
| 216 | +if __name__ == "__main__": |
| 217 | + unittest.main() |
0 commit comments