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,163 @@ 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+ values = [
95+ _testcapi .LONG_MIN , _testcapi .LONG_MAX ,
96+ - limit , - limit + 2 , limit - 2 , limit - 1 ,
97+ 0 , 123 , - 123 ,
98+ ]
99+ # Test values larger than 32-bit on platforms with 64-bit C long
100+ if _testcapi .LONG_MAX > (2 ** 31 - 1 ):
101+ values .extend ((- limit - 2 , limit , limit + 2 ))
102+
103+ for version in range (marshal .version + 1 ):
104+ for value in values :
105+ with self .subTest (value = value , version = version ):
106+ write_long_to_file (value , filename , version )
107+ data = read_file (filename )
108+ self .assertEqual (len (data ), 4 )
109+ value2 = struct .unpack ('<i' , data )[0 ]
110+ self .assertEqual (value2 , mask32 (value ))
54111
55112 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 )
113+ # Test PyMarshal_WriteObjectToFile()
114+ write_object_to_file = _testcapi .pymarshal_write_object_to_file
115+ filename = os_helper .TESTFN
116+ self .addCleanup (os_helper .unlink , filename )
117+
118+ for version in range (marshal .version + 1 ):
119+ for obj in TEST_OBJECTS :
120+ with self .subTest (obj = obj , version = version ):
121+ write_object_to_file (obj , filename , version )
122+ data = read_file (filename )
123+ self .assertEqual (marshal .loads (data ), obj )
124+
125+ with self .assertRaises (SystemError ):
126+ write_object_to_file (NULL , filename , version )
127+
128+ with self .assertRaisesRegex (ValueError , 'unmarshallable object' ):
129+ write_object_to_file (UNMARSHALLABLE , filename , version )
63130
64131 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 ' )
132+ # Test PyMarshal_ReadShortFromFile()
133+ read_short_from_file = _testcapi .pymarshal_read_short_from_file
134+ filename = os_helper .TESTFN
135+ self .addCleanup (os_helper .unlink , filename )
136+
137+ for value in (- 2 ** 15 , 2 ** 15 - 1 , 0 , 123 , - 123 ):
138+ with self .subTest (value = value ):
139+ data = struct .pack ('<h' , value ) + b'xxxx'
140+ write_file (filename , data )
141+ value2 = read_short_from_file (filename )
142+ self .assertEqual (value2 , value )
143+
144+ write_file (filename , b'\x12 ' ) # less than 2 bytes
74145 with self .assertRaises (EOFError ):
75- _testcapi .pymarshal_read_short_from_file (os_helper .TESTFN )
76- os_helper .unlink (os_helper .TESTFN )
146+ read_short_from_file (filename )
77147
78148 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 ' )
149+ # Test PyMarshal_ReadLongFromFile()
150+ read_long_from_file = _testcapi .pymarshal_read_long_from_file
151+ filename = os_helper .TESTFN
152+ self .addCleanup (os_helper .unlink , filename )
153+
154+ for value in (_testcapi .INT_MIN , _testcapi .INT_MAX , 0 , 123 , - 123 ):
155+ with self .subTest (value = value ):
156+ data = struct .pack ('<i' , value )
157+ write_file (filename , data )
158+ value2 = read_long_from_file (filename )
159+ self .assertEqual (value2 , value )
160+
161+ write_file (filename , b'\x56 \x34 \x12 ' ) # less than 4 bytes
162+ with self .assertRaises (EOFError ):
163+ read_long_from_file (filename )
164+
165+ def check_read_object (self , read_object_func , check_pos = True ):
166+ filename = os_helper .TESTFN
167+ self .addCleanup (os_helper .unlink , filename )
168+
169+ version = marshal .version
170+ for obj in TEST_OBJECTS :
171+ with self .subTest (obj = obj ):
172+ data = marshal .dumps (obj , version )
173+ data += b'abc' # following data is ignored
174+ write_file (filename , data )
175+ obj2 , pos = read_object_func (filename )
176+ self .check_object (obj2 , obj )
177+ if check_pos :
178+ self .assertEqual (pos , len (data ))
179+
180+ data = marshal .dumps (SIMPLE_OBJECT , version )
181+ data = data [:- 1 ] # truncate last byte
182+ write_file (filename , data )
88183 with self .assertRaises (EOFError ):
89- _testcapi .pymarshal_read_long_from_file (os_helper .TESTFN )
90- os_helper .unlink (os_helper .TESTFN )
184+ read_object_func (filename )
185+
186+ write_file (filename , JUNK_BYTES )
187+ with self .assertRaisesRegex (ValueError , 'bad marshal data' ):
188+ read_object_func (filename )
91189
92190 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 )
191+ # Test PyMarshal_ReadLastObjectFromFile()
192+ read_last_object_from_file = _testcapi .pymarshal_read_last_object_from_file
193+ self .check_read_object (read_last_object_from_file )
107194
108195 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 )
196+ # Test PyMarshal_ReadObjectFromFile()
197+ read_object_from_file = _testcapi .pymarshal_read_object_from_file
198+ self .check_read_object (read_object_from_file , check_pos = False )
199+
200+ def test_pymarshal_readobjectfromstring (self ):
201+ # Test PyMarshal_ReadObjectFromString()
202+ readobjectfromstring = _testcapi .pymarshal_readobjectfromstring
203+ for obj in TEST_OBJECTS :
204+ for version in range (marshal .version + 1 ):
205+ with self .subTest (obj = obj , version = version ):
206+ data = marshal .dumps (obj , version )
207+ obj2 = readobjectfromstring (data )
208+ self .check_object (obj2 , obj )
209+
210+ data = marshal .dumps (SIMPLE_OBJECT , marshal .version )
211+ data = data [:- 1 ] # truncate last byte
212+ with self .assertRaises (EOFError ):
213+ readobjectfromstring (data )
214+
215+ with self .assertRaisesRegex (ValueError , 'bad marshal data' ):
216+ readobjectfromstring (JUNK_BYTES )
217+
218+ def test_pymarshal_writeobjecttostring (self ):
219+ # Test PyMarshal_WriteObjectToString()
220+ writeobjecttostring = _testcapi .pymarshal_writeobjecttostring
221+ for version in range (marshal .version + 1 ):
222+ for obj in TEST_OBJECTS :
223+ with self .subTest (obj = obj , version = version ):
224+ data = writeobjecttostring (obj , version )
225+ obj2 = marshal .loads (data )
226+ self .check_object (obj2 , obj )
227+
228+ with self .assertRaisesRegex (ValueError , 'unmarshallable object' ):
229+ writeobjecttostring (UNMARSHALLABLE , version )
230+
231+ with self .assertRaises (SystemError ):
232+ writeobjecttostring (NULL , version )
124233
125234
126235if __name__ == "__main__" :
0 commit comments