1 # NOTE: this file tests the new `io` library backported from Python 3.x.
2 # Similar tests for the builtin file object can be found in test_file2k.py.
3 
4 from __future__ import print_function
5 
6 import sys
7 import os
8 import unittest
9 from array import array
10 from weakref import proxy
11 
12 import io
13 import _pyio as pyio
14 
15 from test.support import TESTFN, run_unittest
16 from test import support
17 from UserList import UserList
18 
19 class AutoFileTests(unittest.TestCase):
20     # file tests for which a test file is automatically set up
21 
22     def setUp(self):
23         self.f = self.open(TESTFN, 'wb')
24 
25     def tearDown(self):
26         if self.f:
27             self.f.close()
28         support.unlink(TESTFN)
29 
30     def testWeakRefs(self):
31         # verify weak references
32         p = proxy(self.f)
33         p.write(b'teststring')
34         self.assertEqual(self.f.tell(), p.tell())
35         self.f.close()
36         self.f = None
37         self.assertRaises(ReferenceError, getattr, p, 'tell')
38 
39     def testAttributes(self):
40         # verify expected attributes exist
41         f = self.f
42         f.name     # merely shouldn't blow up
43         f.mode     # ditto
44         f.closed   # ditto
45 
46     def testReadinto(self):
47         # verify readinto
48         self.f.write(b'12')
49         self.f.close()
50         a = array('b', b'x'*10)
51         self.f = self.open(TESTFN, 'rb')
52         n = self.f.readinto(a)
53         self.assertEqual(b'12', a.tostring()[:n])
54 
55     def testReadinto_text(self):
56         # verify readinto refuses text files
57         a = array('b', b'x'*10)
58         self.f.close()
59         self.f = self.open(TESTFN, 'r')
60         if hasattr(self.f, "readinto"):
61             self.assertRaises(TypeError, self.f.readinto, a)
62 
63     def testWritelinesUserList(self):
64         # verify writelines with instance sequence
65         l = UserList([b'1', b'2'])
66         self.f.writelines(l)
67         self.f.close()
68         self.f = self.open(TESTFN, 'rb')
69         buf = self.f.read()
70         self.assertEqual(buf, b'12')
71 
72     def testWritelinesIntegers(self):
73         # verify writelines with integers
74         self.assertRaises(TypeError, self.f.writelines, [1, 2, 3])
75 
76     def testWritelinesIntegersUserList(self):
77         # verify writelines with integers in UserList
78         l = UserList([1,2,3])
79         self.assertRaises(TypeError, self.f.writelines, l)
80 
81     def testWritelinesNonString(self):
82         # verify writelines with non-string object
83         class NonString:
84             pass
85 
86         self.assertRaises(TypeError, self.f.writelines,
87                           [NonString(), NonString()])
88 
89     def testErrors(self):
90         f = self.f
91         self.assertEqual(f.name, TESTFN)
92         self.assertFalse(f.isatty())
93         self.assertFalse(f.closed)
94 
95         if hasattr(f, "readinto"):
96             self.assertRaises((IOError, TypeError), f.readinto, "")
97         f.close()
98         self.assertTrue(f.closed)
99 
100     def testMethods(self):
101         methods = [('fileno', ()),
102                    ('flush', ()),
103                    ('isatty', ()),
104                    ('next', ()),
105                    ('read', ()),
106                    ('write', (b"",)),
107                    ('readline', ()),
108                    ('readlines', ()),
109                    ('seek', (0,)),
110                    ('tell', ()),
111                    ('write', (b"",)),
112                    ('writelines', ([],)),
113                    ('__iter__', ()),
114                    ]
115         if not sys.platform.startswith('atheos'):
116             methods.append(('truncate', ()))
117 
118         # __exit__ should close the file
119         self.f.__exit__(None, None, None)
120         self.assertTrue(self.f.closed)
121 
122         for methodname, args in methods:
123             method = getattr(self.f, methodname)
124             # should raise on closed file
125             self.assertRaises(ValueError, method, *args)
126 
127         # file is closed, __exit__ shouldn't do anything
128         self.assertEqual(self.f.__exit__(None, None, None), None)
129         # it must also return None if an exception was given
130         try:
131             1 // 0
132         except:
133             self.assertEqual(self.f.__exit__(*sys.exc_info()), None)
134 
135     def testReadWhenWriting(self):
136         self.assertRaises(IOError, self.f.read)
137 
138 class CAutoFileTests(AutoFileTests):
139     open = io.open
140 
141 class PyAutoFileTests(AutoFileTests):
142     open = staticmethod(pyio.open)
143 
144 
145 class OtherFileTests(unittest.TestCase):
146 
147     def tearDown(self):
148         support.unlink(TESTFN)
149 
150     def testModeStrings(self):
151         # check invalid mode strings
152         self.open(TESTFN, 'wb').close()
153         for mode in ("", "aU", "wU+"):
154             try:
155                 f = self.open(TESTFN, mode)
156             except ValueError:
157                 pass
158             else:
159                 f.close()
160                 self.fail('%r is an invalid file mode' % mode)
161 
162     def testBadModeArgument(self):
163         # verify that we get a sensible error message for bad mode argument
164         bad_mode = "qwerty"
165         try:
166             f = self.open(TESTFN, bad_mode)
167         except ValueError as msg:
168             if msg.args[0] != 0:
169                 s = str(msg)
170                 if TESTFN in s or bad_mode not in s:
171                     self.fail("bad error message for invalid mode: %s" % s)
172             # if msg.args[0] == 0, we're probably on Windows where there may be
173             # no obvious way to discover why open() failed.
174         else:
175             f.close()
176             self.fail("no error for invalid mode: %s" % bad_mode)
177 
178     def testSetBufferSize(self):
179         # make sure that explicitly setting the buffer size doesn't cause
180         # misbehaviour especially with repeated close() calls
181         for s in (-1, 0, 1, 512):
182             try:
183                 f = self.open(TESTFN, 'wb', s)
184                 f.write(str(s).encode("ascii"))
185                 f.close()
186                 f.close()
187                 f = self.open(TESTFN, 'rb', s)
188                 d = int(f.read().decode("ascii"))
189                 f.close()
190                 f.close()
191             except IOError as msg:
192                 self.fail('error setting buffer size %d: %s' % (s, str(msg)))
193             self.assertEqual(d, s)
194 
195     def testTruncateOnWindows(self):
196         # SF bug <http://www.python.org/sf/801631>
197         # "file.truncate fault on windows"
198 
199         f = self.open(TESTFN, 'wb')
200 
201         try:
202             f.write(b'12345678901')   # 11 bytes
203             f.close()
204 
205             f = self.open(TESTFN,'rb+')
206             data = f.read(5)
207             if data != b'12345':
208                 self.fail("Read on file opened for update failed %r" % data)
209             if f.tell() != 5:
210                 self.fail("File pos after read wrong %d" % f.tell())
211 
212             f.truncate()
213             if f.tell() != 5:
214                 self.fail("File pos after ftruncate wrong %d" % f.tell())
215 
216             f.close()
217             size = os.path.getsize(TESTFN)
218             if size != 5:
219                 self.fail("File size after ftruncate wrong %d" % size)
220         finally:
221             f.close()
222 
223     def testIteration(self):
224         # Test the complex interaction when mixing file-iteration and the
225         # various read* methods.
226         dataoffset = 16384
227         filler = b"ham\n"
228         assert not dataoffset % len(filler), \
229             "dataoffset must be multiple of len(filler)"
230         nchunks = dataoffset // len(filler)
231         testlines = [
232             b"spam, spam and eggs\n",
233             b"eggs, spam, ham and spam\n",
234             b"saussages, spam, spam and eggs\n",
235             b"spam, ham, spam and eggs\n",
236             b"spam, spam, spam, spam, spam, ham, spam\n",
237             b"wonderful spaaaaaam.\n"
238         ]
239         methods = [("readline", ()), ("read", ()), ("readlines", ()),
240                    ("readinto", (array("b", b" "*100),))]
241 
242         # Prepare the testfile
243         bag = self.open(TESTFN, "wb")
244         bag.write(filler * nchunks)
245         bag.writelines(testlines)
246         bag.close()
247         # Test for appropriate errors mixing read* and iteration
248         for methodname, args in methods:
249             f = self.open(TESTFN, 'rb')
250             self.assertEqual(next(f), filler)
251             meth = getattr(f, methodname)
252             meth(*args)  # This simply shouldn't fail
253             f.close()
254 
255         # Test to see if harmless (by accident) mixing of read* and
256         # iteration still works. This depends on the size of the internal
257         # iteration buffer (currently 8192,) but we can test it in a
258         # flexible manner.  Each line in the bag o' ham is 4 bytes
259         # ("h", "a", "m", "\n"), so 4096 lines of that should get us
260         # exactly on the buffer boundary for any power-of-2 buffersize
261         # between 4 and 16384 (inclusive).
262         f = self.open(TESTFN, 'rb')
263         for i in range(nchunks):
264             next(f)
265         testline = testlines.pop(0)
266         try:
267             line = f.readline()
268         except ValueError:
269             self.fail("readline() after next() with supposedly empty "
270                         "iteration-buffer failed anyway")
271         if line != testline:
272             self.fail("readline() after next() with empty buffer "
273                         "failed. Got %r, expected %r" % (line, testline))
274         testline = testlines.pop(0)
275         buf = array("b", b"\x00" * len(testline))
276         try:
277             f.readinto(buf)
278         except ValueError:
279             self.fail("readinto() after next() with supposedly empty "
280                         "iteration-buffer failed anyway")
281         line = buf.tostring()
282         if line != testline:
283             self.fail("readinto() after next() with empty buffer "
284                         "failed. Got %r, expected %r" % (line, testline))
285 
286         testline = testlines.pop(0)
287         try:
288             line = f.read(len(testline))
289         except ValueError:
290             self.fail("read() after next() with supposedly empty "
291                         "iteration-buffer failed anyway")
292         if line != testline:
293             self.fail("read() after next() with empty buffer "
294                         "failed. Got %r, expected %r" % (line, testline))
295         try:
296             lines = f.readlines()
297         except ValueError:
298             self.fail("readlines() after next() with supposedly empty "
299                         "iteration-buffer failed anyway")
300         if lines != testlines:
301             self.fail("readlines() after next() with empty buffer "
302                         "failed. Got %r, expected %r" % (line, testline))
303         # Reading after iteration hit EOF shouldn't hurt either
304         f.close()
305         f = self.open(TESTFN, 'rb')
306         try:
307             for line in f:
308                 pass
309             try:
310                 f.readline()
311                 f.readinto(buf)
312                 f.read()
313                 f.readlines()
314             except ValueError:
315                 self.fail("read* failed after next() consumed file")
316         finally:
317             f.close()
318 
319 class COtherFileTests(OtherFileTests):
320     open = io.open
321 
322 class PyOtherFileTests(OtherFileTests):
323     open = staticmethod(pyio.open)
324 
325 
326 def test_main():
327     run_unittest(CAutoFileTests, PyAutoFileTests,
328                  COtherFileTests, PyOtherFileTests)
329 
330 if __name__ == '__main__':
331     test_main()
332