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