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 4from __future__ import print_function 5 6import sys 7import os 8import unittest 9from array import array 10from weakref import proxy 11 12import io 13import _pyio as pyio 14 15from test.test_support import TESTFN, run_unittest 16from UserList import UserList 17 18class 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.assertFalse(f.isatty()) 92 self.assertFalse(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 137class CAutoFileTests(AutoFileTests): 138 open = io.open 139 140class PyAutoFileTests(AutoFileTests): 141 open = staticmethod(pyio.open) 142 143 144class 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 testBadModeArgument(self): 158 # verify that we get a sensible error message for bad mode argument 159 bad_mode = "qwerty" 160 try: 161 f = self.open(TESTFN, bad_mode) 162 except ValueError as msg: 163 if msg.args[0] != 0: 164 s = str(msg) 165 if TESTFN in s or bad_mode not in s: 166 self.fail("bad error message for invalid mode: %s" % s) 167 # if msg.args[0] == 0, we're probably on Windows where there may be 168 # no obvious way to discover why open() failed. 169 else: 170 f.close() 171 self.fail("no error for invalid mode: %s" % bad_mode) 172 173 def testSetBufferSize(self): 174 # make sure that explicitly setting the buffer size doesn't cause 175 # misbehaviour especially with repeated close() calls 176 for s in (-1, 0, 1, 512): 177 try: 178 f = self.open(TESTFN, 'wb', s) 179 f.write(str(s).encode("ascii")) 180 f.close() 181 f.close() 182 f = self.open(TESTFN, 'rb', s) 183 d = int(f.read().decode("ascii")) 184 f.close() 185 f.close() 186 except IOError as msg: 187 self.fail('error setting buffer size %d: %s' % (s, str(msg))) 188 self.assertEqual(d, s) 189 190 def testTruncateOnWindows(self): 191 # SF bug <http://www.python.org/sf/801631> 192 # "file.truncate fault on windows" 193 194 os.unlink(TESTFN) 195 f = self.open(TESTFN, 'wb') 196 197 try: 198 f.write(b'12345678901') # 11 bytes 199 f.close() 200 201 f = self.open(TESTFN,'rb+') 202 data = f.read(5) 203 if data != b'12345': 204 self.fail("Read on file opened for update failed %r" % data) 205 if f.tell() != 5: 206 self.fail("File pos after read wrong %d" % f.tell()) 207 208 f.truncate() 209 if f.tell() != 5: 210 self.fail("File pos after ftruncate wrong %d" % f.tell()) 211 212 f.close() 213 size = os.path.getsize(TESTFN) 214 if size != 5: 215 self.fail("File size after ftruncate wrong %d" % size) 216 finally: 217 f.close() 218 os.unlink(TESTFN) 219 220 def testIteration(self): 221 # Test the complex interaction when mixing file-iteration and the 222 # various read* methods. 223 dataoffset = 16384 224 filler = b"ham\n" 225 assert not dataoffset % len(filler), \ 226 "dataoffset must be multiple of len(filler)" 227 nchunks = dataoffset // len(filler) 228 testlines = [ 229 b"spam, spam and eggs\n", 230 b"eggs, spam, ham and spam\n", 231 b"saussages, spam, spam and eggs\n", 232 b"spam, ham, spam and eggs\n", 233 b"spam, spam, spam, spam, spam, ham, spam\n", 234 b"wonderful spaaaaaam.\n" 235 ] 236 methods = [("readline", ()), ("read", ()), ("readlines", ()), 237 ("readinto", (array("b", b" "*100),))] 238 239 try: 240 # Prepare the testfile 241 bag = self.open(TESTFN, "wb") 242 bag.write(filler * nchunks) 243 bag.writelines(testlines) 244 bag.close() 245 # Test for appropriate errors mixing read* and iteration 246 for methodname, args in methods: 247 f = self.open(TESTFN, 'rb') 248 if next(f) != filler: 249 self.fail, "Broken testfile" 250 meth = getattr(f, methodname) 251 meth(*args) # This simply shouldn't fail 252 f.close() 253 254 # Test to see if harmless (by accident) mixing of read* and 255 # iteration still works. This depends on the size of the internal 256 # iteration buffer (currently 8192,) but we can test it in a 257 # flexible manner. Each line in the bag o' ham is 4 bytes 258 # ("h", "a", "m", "\n"), so 4096 lines of that should get us 259 # exactly on the buffer boundary for any power-of-2 buffersize 260 # between 4 and 16384 (inclusive). 261 f = self.open(TESTFN, 'rb') 262 for i in range(nchunks): 263 next(f) 264 testline = testlines.pop(0) 265 try: 266 line = f.readline() 267 except ValueError: 268 self.fail("readline() after next() with supposedly empty " 269 "iteration-buffer failed anyway") 270 if line != testline: 271 self.fail("readline() after next() with empty buffer " 272 "failed. Got %r, expected %r" % (line, testline)) 273 testline = testlines.pop(0) 274 buf = array("b", b"\x00" * len(testline)) 275 try: 276 f.readinto(buf) 277 except ValueError: 278 self.fail("readinto() after next() with supposedly empty " 279 "iteration-buffer failed anyway") 280 line = buf.tostring() 281 if line != testline: 282 self.fail("readinto() after next() with empty buffer " 283 "failed. Got %r, expected %r" % (line, testline)) 284 285 testline = testlines.pop(0) 286 try: 287 line = f.read(len(testline)) 288 except ValueError: 289 self.fail("read() after next() with supposedly empty " 290 "iteration-buffer failed anyway") 291 if line != testline: 292 self.fail("read() after next() with empty buffer " 293 "failed. Got %r, expected %r" % (line, testline)) 294 try: 295 lines = f.readlines() 296 except ValueError: 297 self.fail("readlines() after next() with supposedly empty " 298 "iteration-buffer failed anyway") 299 if lines != testlines: 300 self.fail("readlines() after next() with empty buffer " 301 "failed. Got %r, expected %r" % (line, testline)) 302 # Reading after iteration hit EOF shouldn't hurt either 303 f.close() 304 f = self.open(TESTFN, 'rb') 305 try: 306 for line in f: 307 pass 308 try: 309 f.readline() 310 f.readinto(buf) 311 f.read() 312 f.readlines() 313 except ValueError: 314 self.fail("read* failed after next() consumed file") 315 finally: 316 f.close() 317 finally: 318 os.unlink(TESTFN) 319 320class COtherFileTests(OtherFileTests): 321 open = io.open 322 323class PyOtherFileTests(OtherFileTests): 324 open = staticmethod(pyio.open) 325 326 327def test_main(): 328 # Historically, these tests have been sloppy about removing TESTFN. 329 # So get rid of it no matter what. 330 try: 331 run_unittest(CAutoFileTests, PyAutoFileTests, 332 COtherFileTests, PyOtherFileTests) 333 finally: 334 if os.path.exists(TESTFN): 335 os.unlink(TESTFN) 336 337if __name__ == '__main__': 338 test_main() 339