1+ # Test PyMarshal C API
2+
13import marshal
24import os .path
5+ import struct
36import unittest
47
58from test import support
69from test .support import import_helper
710from test .support import os_helper
8- from test .test_marshal import HelperMixin , omit_last_byte
911
1012
1113# Skip this test if _testcapi is are not available.
1214_testcapi = import_helper .import_module ('_testcapi' )
1315
1416
17+ def noop_func ():
18+ pass
19+
20+ NULL = None
21+ SIMPLE_OBJECT = 123
22+ # Only test a few objects: see test_marshal for more exhaustive tests
23+ TEST_OBJECTS = (
24+ '\u20ac ' ,
25+ b'abc' ,
26+ True ,
27+ 123 ,
28+ 45.6 ,
29+ 7 + 8j ,
30+ 'long line ' * 1000 ,
31+ # Check that serializing code object is allowed (allow_code = 1)
32+ noop_func .__code__ ,
33+ )
34+ UNMARSHALLABLE = object ()
35+
36+ # Invalid marshal data
37+ JUNK_BYTES = b'\xff ' * 32
38+
39+
40+ def read_file (filename ):
41+ with open (filename , 'rb' ) as fp :
42+ return fp .read ()
43+
44+
45+ def write_file (filename , data ):
46+ with open (filename , 'wb' ) as fp :
47+ fp .write (data )
48+
49+
1550@support .cpython_only
16- class CAPI_TestCase (unittest .TestCase , HelperMixin ):
51+ class CAPI_TestCase (unittest .TestCase ):
1752
1853 def test_read_from_file_error (self ):
1954 # A read error is reported as OSError, not EOFError.
@@ -38,89 +73,159 @@ def test_write_to_file_error(self):
3873 _testcapi .pymarshal_write_object_to_file (obj , '/dev/full' ,
3974 marshal .version )
4075
41- def test_write_unmarshallable_to_file (self ):
42- self .addCleanup (os_helper .unlink , os_helper .TESTFN )
43- with self .assertRaisesRegex (ValueError , 'unmarshallable object' ):
44- _testcapi .pymarshal_write_object_to_file (object (), os_helper .TESTFN ,
45- marshal .version )
76+ def check_object (self , obj2 , obj ):
77+ self .assertEqual (obj2 , obj )
78+ self .assertEqual (type (obj2 ), type (obj ))
4679
4780 def test_write_long_to_file (self ):
48- for v in range (marshal .version + 1 ):
49- _testcapi .pymarshal_write_long_to_file (0x12345678 , os_helper .TESTFN , v )
50- with open (os_helper .TESTFN , 'rb' ) as f :
51- data = f .read ()
52- os_helper .unlink (os_helper .TESTFN )
53- self .assertEqual (data , b'\x78 \x56 \x34 \x12 ' )
81+ # Test PyMarshal_WriteLongToFile()
82+ write_long_to_file = _testcapi .pymarshal_write_long_to_file
83+ filename = os_helper .TESTFN
84+ self .addCleanup (os_helper .unlink , filename )
85+
86+ def mask32 (value ):
87+ res = value & (2 ** 32 - 1 )
88+ if res >= 2147483648 :
89+ return res - 4294967296
90+ else :
91+ return res
92+
93+ limit = 2 ** 31
94+ for version in range (marshal .version + 1 ):
95+ for value in (
96+ _testcapi .LONG_MIN , _testcapi .LONG_MAX ,
97+ - limit - 2 , - limit , - limit + 2 ,
98+ limit - 2 , limit , limit + 2 ,
99+ 0 , 123 , - 123 ,
100+ ):
101+ with self .subTest (value = value , version = version ):
102+ write_long_to_file (value , filename , version )
103+ data = read_file (filename )
104+ self .assertEqual (len (data ), 4 )
105+ value2 = struct .unpack ('<i' , data )[0 ]
106+ self .assertEqual (value2 , mask32 (value ))
54107
55108 def test_write_object_to_file (self ):
56- obj = ('\u20ac ' , b'abc' , 123 , 45.6 , 7 + 8j , 'long line ' * 1000 )
57- for v in range (marshal .version + 1 ):
58- _testcapi .pymarshal_write_object_to_file (obj , os_helper .TESTFN , v )
59- with open (os_helper .TESTFN , 'rb' ) as f :
60- data = f .read ()
61- os_helper .unlink (os_helper .TESTFN )
62- self .assertEqual (marshal .loads (data ), obj )
109+ # Test PyMarshal_WriteObjectToFile()
110+ write_object_to_file = _testcapi .pymarshal_write_object_to_file
111+ filename = os_helper .TESTFN
112+ self .addCleanup (os_helper .unlink , filename )
113+
114+ for version in range (marshal .version + 1 ):
115+ for obj in TEST_OBJECTS :
116+ with self .subTest (obj = obj , version = version ):
117+ write_object_to_file (obj , filename , version )
118+ data = read_file (filename )
119+ self .assertEqual (marshal .loads (data ), obj )
120+
121+ with self .assertRaises (SystemError ):
122+ write_object_to_file (NULL , filename , version )
123+
124+ with self .assertRaisesRegex (ValueError , 'unmarshallable object' ):
125+ write_object_to_file (UNMARSHALLABLE , filename , version )
63126
64127 def test_read_short_from_file (self ):
65- with open (os_helper .TESTFN , 'wb' ) as f :
66- f .write (b'\x34 \x12 xxxx' )
67- r , p = _testcapi .pymarshal_read_short_from_file (os_helper .TESTFN )
68- os_helper .unlink (os_helper .TESTFN )
69- self .assertEqual (r , 0x1234 )
70- self .assertEqual (p , 2 )
71-
72- with open (os_helper .TESTFN , 'wb' ) as f :
73- f .write (b'\x12 ' )
128+ # Test PyMarshal_ReadShortFromFile()
129+ read_short_from_file = _testcapi .pymarshal_read_short_from_file
130+ filename = os_helper .TESTFN
131+ self .addCleanup (os_helper .unlink , filename )
132+
133+ for value in (- 2 ** 15 , 2 ** 15 - 1 , 0 , 123 , - 123 ):
134+ with self .subTest (value = value ):
135+ data = struct .pack ('<h' , value ) + b'xxxx'
136+ write_file (filename , data )
137+ value2 = read_short_from_file (filename )
138+ self .assertEqual (value2 , value )
139+
140+ write_file (filename , b'\x12 ' ) # less than 2 bytes
74141 with self .assertRaises (EOFError ):
75- _testcapi .pymarshal_read_short_from_file (os_helper .TESTFN )
76- os_helper .unlink (os_helper .TESTFN )
142+ read_short_from_file (filename )
77143
78144 def test_read_long_from_file (self ):
79- with open (os_helper .TESTFN , 'wb' ) as f :
80- f .write (b'\x78 \x56 \x34 \x12 xxxx' )
81- r , p = _testcapi .pymarshal_read_long_from_file (os_helper .TESTFN )
82- os_helper .unlink (os_helper .TESTFN )
83- self .assertEqual (r , 0x12345678 )
84- self .assertEqual (p , 4 )
85-
86- with open (os_helper .TESTFN , 'wb' ) as f :
87- f .write (b'\x56 \x34 \x12 ' )
145+ # Test PyMarshal_ReadLongFromFile()
146+ read_long_from_file = _testcapi .pymarshal_read_long_from_file
147+ filename = os_helper .TESTFN
148+ self .addCleanup (os_helper .unlink , filename )
149+
150+ for value in (_testcapi .INT_MIN , _testcapi .INT_MAX , 0 , 123 , - 123 ):
151+ with self .subTest (value = value ):
152+ data = struct .pack ('<i' , value )
153+ write_file (filename , data )
154+ value2 = read_long_from_file (filename )
155+ self .assertEqual (value2 , value )
156+
157+ write_file (filename , b'\x56 \x34 \x12 ' ) # less than 4 bytes
158+ with self .assertRaises (EOFError ):
159+ read_long_from_file (filename )
160+
161+ def check_read_object (self , read_object_func , check_pos = True ):
162+ filename = os_helper .TESTFN
163+ self .addCleanup (os_helper .unlink , filename )
164+
165+ version = marshal .version
166+ for obj in TEST_OBJECTS :
167+ with self .subTest (obj = obj ):
168+ data = marshal .dumps (obj , version )
169+ data += b'abc' # following data is ignored
170+ write_file (filename , data )
171+ obj2 , pos = read_object_func (filename )
172+ self .check_object (obj2 , obj )
173+ if check_pos :
174+ self .assertEqual (pos , len (data ))
175+
176+ data = marshal .dumps (SIMPLE_OBJECT , version )
177+ data = data [:- 1 ] # truncate last byte
178+ write_file (filename , data )
88179 with self .assertRaises (EOFError ):
89- _testcapi .pymarshal_read_long_from_file (os_helper .TESTFN )
90- os_helper .unlink (os_helper .TESTFN )
180+ read_object_func (filename )
181+
182+ write_file (filename , JUNK_BYTES )
183+ with self .assertRaisesRegex (ValueError , 'bad marshal data' ):
184+ read_object_func (filename )
91185
92186 def test_read_last_object_from_file (self ):
93- obj = ('\u20ac ' , b'abc' , 123 , 45.6 , 7 + 8j )
94- for v in range (marshal .version + 1 ):
95- data = marshal .dumps (obj , v )
96- with open (os_helper .TESTFN , 'wb' ) as f :
97- f .write (data + b'xxxx' )
98- r , p = _testcapi .pymarshal_read_last_object_from_file (os_helper .TESTFN )
99- os_helper .unlink (os_helper .TESTFN )
100- self .assertEqual (r , obj )
101-
102- with open (os_helper .TESTFN , 'wb' ) as f :
103- f .write (omit_last_byte (data ))
104- with self .assertRaises (EOFError ):
105- _testcapi .pymarshal_read_last_object_from_file (os_helper .TESTFN )
106- os_helper .unlink (os_helper .TESTFN )
187+ # Test PyMarshal_ReadLastObjectFromFile()
188+ read_last_object_from_file = _testcapi .pymarshal_read_last_object_from_file
189+ self .check_read_object (read_last_object_from_file )
107190
108191 def test_read_object_from_file (self ):
109- obj = ('\u20ac ' , b'abc' , 123 , 45.6 , 7 + 8j )
110- for v in range (marshal .version + 1 ):
111- data = marshal .dumps (obj , v )
112- with open (os_helper .TESTFN , 'wb' ) as f :
113- f .write (data + b'xxxx' )
114- r , p = _testcapi .pymarshal_read_object_from_file (os_helper .TESTFN )
115- os_helper .unlink (os_helper .TESTFN )
116- self .assertEqual (r , obj )
117- self .assertEqual (p , len (data ))
118-
119- with open (os_helper .TESTFN , 'wb' ) as f :
120- f .write (omit_last_byte (data ))
121- with self .assertRaises (EOFError ):
122- _testcapi .pymarshal_read_object_from_file (os_helper .TESTFN )
123- os_helper .unlink (os_helper .TESTFN )
192+ # Test PyMarshal_ReadObjectFromFile()
193+ read_object_from_file = _testcapi .pymarshal_read_object_from_file
194+ self .check_read_object (read_object_from_file , check_pos = False )
195+
196+ def test_pymarshal_readobjectfromstring (self ):
197+ # Test PyMarshal_ReadObjectFromString()
198+ readobjectfromstring = _testcapi .pymarshal_readobjectfromstring
199+ for obj in TEST_OBJECTS :
200+ for version in range (marshal .version + 1 ):
201+ with self .subTest (obj = obj , version = version ):
202+ data = marshal .dumps (obj , version )
203+ obj2 = readobjectfromstring (data )
204+ self .check_object (obj2 , obj )
205+
206+ data = marshal .dumps (SIMPLE_OBJECT , marshal .version )
207+ data = data [:- 1 ] # truncate last byte
208+ with self .assertRaises (EOFError ):
209+ readobjectfromstring (data )
210+
211+ with self .assertRaisesRegex (ValueError , 'bad marshal data' ):
212+ readobjectfromstring (JUNK_BYTES )
213+
214+ def test_pymarshal_writeobjecttostring (self ):
215+ # Test PyMarshal_WriteObjectToString()
216+ writeobjecttostring = _testcapi .pymarshal_writeobjecttostring
217+ for version in range (marshal .version + 1 ):
218+ for obj in TEST_OBJECTS :
219+ with self .subTest (obj = obj , version = version ):
220+ data = writeobjecttostring (obj , version )
221+ obj2 = marshal .loads (data )
222+ self .check_object (obj2 , obj )
223+
224+ with self .assertRaisesRegex (ValueError , 'unmarshallable object' ):
225+ writeobjecttostring (UNMARSHALLABLE , version )
226+
227+ with self .assertRaises (SystemError ):
228+ writeobjecttostring (NULL , version )
124229
125230
126231if __name__ == "__main__" :
0 commit comments