blob: 6c8813e062602edd64f6c9054e5af32fa454370a [file] [log] [blame]
Christian Heimes77c02eb2008-02-09 02:18:51 +00001"""Test largefile support on system where this makes sense.
2"""
Trent Mickf29f47b2000-08-11 19:02:59 +00003
Christian Heimes77c02eb2008-02-09 02:18:51 +00004import os
5import stat
6import sys
7import unittest
Giampaolo Rodola5bcc6d82019-09-30 12:51:55 +08008import socket
9import shutil
10import threading
11from test.support import TESTFN, requires, unlink, bigmemtest, find_unused_port
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +000012import io # C implementation of io
13import _pyio as pyio # Python implementation of io
Trent Mickf29f47b2000-08-11 19:02:59 +000014
Victor Stinner8c663fd2017-11-08 14:44:44 -080015# size of file to create (>2 GiB; 2 GiB == 2,147,483,648 bytes)
Stéphane Wirtel74a8b6e2018-10-18 01:05:04 +020016size = 2_500_000_000
Giampaolo Rodola5bcc6d82019-09-30 12:51:55 +080017TESTFN2 = TESTFN + '2'
18
Guido van Rossuma31ddbb2001-09-10 15:03:18 +000019
Serhiy Storchakac406a122013-07-17 13:42:24 +030020class LargeFileTest:
Christian Heimes77c02eb2008-02-09 02:18:51 +000021
Serhiy Storchakac406a122013-07-17 13:42:24 +030022 def setUp(self):
23 if os.path.exists(TESTFN):
24 mode = 'r+b'
25 else:
26 mode = 'w+b'
27
28 with self.open(TESTFN, mode) as f:
29 current_size = os.fstat(f.fileno())[stat.ST_SIZE]
30 if current_size == size+1:
31 return
32
33 if current_size == 0:
34 f.write(b'z')
35
Christian Heimes77c02eb2008-02-09 02:18:51 +000036 f.seek(0)
37 f.seek(size)
Alexandre Vassalottia351f772008-03-03 02:59:49 +000038 f.write(b'a')
Christian Heimes77c02eb2008-02-09 02:18:51 +000039 f.flush()
Christian Heimes77c02eb2008-02-09 02:18:51 +000040 self.assertEqual(os.fstat(f.fileno())[stat.ST_SIZE], size+1)
Christian Heimes77c02eb2008-02-09 02:18:51 +000041
Serhiy Storchakac406a122013-07-17 13:42:24 +030042 @classmethod
43 def tearDownClass(cls):
44 with cls.open(TESTFN, 'wb'):
45 pass
46 if not os.stat(TESTFN)[stat.ST_SIZE] == 0:
47 raise cls.failureException('File was not truncated by opening '
48 'with mode "wb"')
Giampaolo Rodola5bcc6d82019-09-30 12:51:55 +080049 unlink(TESTFN2)
50
51
52class TestFileMethods(LargeFileTest):
53 """Test that each file function works as expected for large
54 (i.e. > 2 GiB) files.
55 """
Serhiy Storchakac406a122013-07-17 13:42:24 +030056
Stéphane Wirtel74a8b6e2018-10-18 01:05:04 +020057 # _pyio.FileIO.readall() uses a temporary bytearray then casted to bytes,
58 # so memuse=2 is needed
59 @bigmemtest(size=size, memuse=2, dry_run=False)
60 def test_large_read(self, _size):
61 # bpo-24658: Test that a read greater than 2GB does not fail.
62 with self.open(TESTFN, "rb") as f:
63 self.assertEqual(len(f.read()), size + 1)
64 self.assertEqual(f.tell(), size + 1)
65
Christian Heimes77c02eb2008-02-09 02:18:51 +000066 def test_osstat(self):
Christian Heimes77c02eb2008-02-09 02:18:51 +000067 self.assertEqual(os.stat(TESTFN)[stat.ST_SIZE], size+1)
68
69 def test_seek_read(self):
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +000070 with self.open(TESTFN, 'rb') as f:
Christian Heimes77c02eb2008-02-09 02:18:51 +000071 self.assertEqual(f.tell(), 0)
Alexandre Vassalottia351f772008-03-03 02:59:49 +000072 self.assertEqual(f.read(1), b'z')
Christian Heimes77c02eb2008-02-09 02:18:51 +000073 self.assertEqual(f.tell(), 1)
74 f.seek(0)
75 self.assertEqual(f.tell(), 0)
76 f.seek(0, 0)
77 self.assertEqual(f.tell(), 0)
78 f.seek(42)
79 self.assertEqual(f.tell(), 42)
80 f.seek(42, 0)
81 self.assertEqual(f.tell(), 42)
82 f.seek(42, 1)
83 self.assertEqual(f.tell(), 84)
84 f.seek(0, 1)
85 self.assertEqual(f.tell(), 84)
86 f.seek(0, 2) # seek from the end
87 self.assertEqual(f.tell(), size + 1 + 0)
88 f.seek(-10, 2)
89 self.assertEqual(f.tell(), size + 1 - 10)
90 f.seek(-size-1, 2)
91 self.assertEqual(f.tell(), 0)
92 f.seek(size)
93 self.assertEqual(f.tell(), size)
94 # the 'a' that was written at the end of file above
Alexandre Vassalottia351f772008-03-03 02:59:49 +000095 self.assertEqual(f.read(1), b'a')
Christian Heimes77c02eb2008-02-09 02:18:51 +000096 f.seek(-size-1, 1)
Alexandre Vassalottia351f772008-03-03 02:59:49 +000097 self.assertEqual(f.read(1), b'z')
Christian Heimes77c02eb2008-02-09 02:18:51 +000098 self.assertEqual(f.tell(), 1)
Christian Heimes77c02eb2008-02-09 02:18:51 +000099
100 def test_lseek(self):
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000101 with self.open(TESTFN, 'rb') as f:
Christian Heimes77c02eb2008-02-09 02:18:51 +0000102 self.assertEqual(os.lseek(f.fileno(), 0, 0), 0)
103 self.assertEqual(os.lseek(f.fileno(), 42, 0), 42)
104 self.assertEqual(os.lseek(f.fileno(), 42, 1), 84)
105 self.assertEqual(os.lseek(f.fileno(), 0, 1), 84)
106 self.assertEqual(os.lseek(f.fileno(), 0, 2), size+1+0)
107 self.assertEqual(os.lseek(f.fileno(), -10, 2), size+1-10)
108 self.assertEqual(os.lseek(f.fileno(), -size-1, 2), 0)
109 self.assertEqual(os.lseek(f.fileno(), size, 0), size)
110 # the 'a' that was written at the end of file above
Alexandre Vassalottia351f772008-03-03 02:59:49 +0000111 self.assertEqual(f.read(1), b'a')
Christian Heimes77c02eb2008-02-09 02:18:51 +0000112
113 def test_truncate(self):
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000114 with self.open(TESTFN, 'r+b') as f:
Christian Heimes180510d2008-03-03 19:15:45 +0000115 if not hasattr(f, 'truncate'):
Serhiy Storchakac406a122013-07-17 13:42:24 +0300116 raise unittest.SkipTest("open().truncate() not available "
117 "on this system")
Christian Heimes77c02eb2008-02-09 02:18:51 +0000118 f.seek(0, 2)
119 # else we've lost track of the true size
120 self.assertEqual(f.tell(), size+1)
121 # Cut it back via seek + truncate with no argument.
122 newsize = size - 10
123 f.seek(newsize)
124 f.truncate()
125 self.assertEqual(f.tell(), newsize) # else pointer moved
126 f.seek(0, 2)
127 self.assertEqual(f.tell(), newsize) # else wasn't truncated
128 # Ensure that truncate(smaller than true size) shrinks
129 # the file.
130 newsize -= 1
131 f.seek(42)
132 f.truncate(newsize)
Antoine Pitrou905a2ff2010-01-31 22:47:27 +0000133 self.assertEqual(f.tell(), 42)
Alexandre Vassalotti77250f42008-05-06 19:48:38 +0000134 f.seek(0, 2)
135 self.assertEqual(f.tell(), newsize)
Christian Heimes77c02eb2008-02-09 02:18:51 +0000136 # XXX truncate(larger than true size) is ill-defined
137 # across platform; cut it waaaaay back
138 f.seek(0)
139 f.truncate(1)
Antoine Pitrou905a2ff2010-01-31 22:47:27 +0000140 self.assertEqual(f.tell(), 0) # else pointer moved
Alexandre Vassalotti77250f42008-05-06 19:48:38 +0000141 f.seek(0)
Christian Heimes77c02eb2008-02-09 02:18:51 +0000142 self.assertEqual(len(f.read()), 1) # else wasn't truncated
Christian Heimes77c02eb2008-02-09 02:18:51 +0000143
Antoine Pitroua28fcfd2009-03-13 23:42:55 +0000144 def test_seekable(self):
145 # Issue #5016; seekable() can return False when the current position
146 # is negative when truncated to an int.
147 for pos in (2**31-1, 2**31, 2**31+1):
148 with self.open(TESTFN, 'rb') as f:
149 f.seek(pos)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000150 self.assertTrue(f.seekable())
Antoine Pitroua28fcfd2009-03-13 23:42:55 +0000151
Giampaolo Rodola5bcc6d82019-09-30 12:51:55 +0800152
153class TestCopyfile(LargeFileTest, unittest.TestCase):
154 open = staticmethod(io.open)
155
156 def test_it(self):
157 # Internally shutil.copyfile() can use "fast copy" methods like
158 # os.sendfile().
159 size = os.path.getsize(TESTFN)
160 shutil.copyfile(TESTFN, TESTFN2)
161 self.assertEqual(os.path.getsize(TESTFN2), size)
162 with open(TESTFN2, 'rb') as f:
163 self.assertEqual(f.read(5), b'z\x00\x00\x00\x00')
164 f.seek(size - 5)
165 self.assertEqual(f.read(), b'\x00\x00\x00\x00a')
166
167
168@unittest.skipIf(not hasattr(os, 'sendfile'), 'sendfile not supported')
169class TestSocketSendfile(LargeFileTest, unittest.TestCase):
170 open = staticmethod(io.open)
171 timeout = 3
172
173 def setUp(self):
174 super().setUp()
175 self.thread = None
176
177 def tearDown(self):
178 super().tearDown()
179 if self.thread is not None:
180 self.thread.join(self.timeout)
181 self.thread = None
182
183 def tcp_server(self, sock):
184 def run(sock):
185 with sock:
186 conn, _ = sock.accept()
187 with conn, open(TESTFN2, 'wb') as f:
188 event.wait(self.timeout)
189 while True:
190 chunk = conn.recv(65536)
191 if not chunk:
192 return
193 f.write(chunk)
194
195 event = threading.Event()
196 sock.settimeout(self.timeout)
197 self.thread = threading.Thread(target=run, args=(sock, ))
198 self.thread.start()
199 event.set()
200
201 def test_it(self):
202 port = find_unused_port()
203 with socket.create_server(("", port)) as sock:
204 self.tcp_server(sock)
205 with socket.create_connection(("127.0.0.1", port)) as client:
206 with open(TESTFN, 'rb') as f:
207 client.sendfile(f)
208 self.tearDown()
209
210 size = os.path.getsize(TESTFN)
211 self.assertEqual(os.path.getsize(TESTFN2), size)
212 with open(TESTFN2, 'rb') as f:
213 self.assertEqual(f.read(5), b'z\x00\x00\x00\x00')
214 f.seek(size - 5)
215 self.assertEqual(f.read(), b'\x00\x00\x00\x00a')
216
217
Serhiy Storchakac406a122013-07-17 13:42:24 +0300218def setUpModule():
219 try:
220 import signal
221 # The default handler for SIGXFSZ is to abort the process.
222 # By ignoring it, system calls exceeding the file size resource
223 # limit will raise OSError instead of crashing the interpreter.
224 signal.signal(signal.SIGXFSZ, signal.SIG_IGN)
225 except (ImportError, AttributeError):
226 pass
Antoine Pitroua28fcfd2009-03-13 23:42:55 +0000227
Mike53f7a7c2017-12-14 14:04:53 +0300228 # On Windows and Mac OSX this test consumes large resources; It
Victor Stinner8c663fd2017-11-08 14:44:44 -0800229 # takes a long time to build the >2 GiB file and takes >2 GiB of disk
Christian Heimes77c02eb2008-02-09 02:18:51 +0000230 # space therefore the resource must be enabled to run this test.
231 # If not, nothing after this line stanza will be executed.
Victor Stinner937ee9e2018-06-26 02:11:06 +0200232 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
Christian Heimes77c02eb2008-02-09 02:18:51 +0000233 requires('largefile',
234 'test requires %s bytes and a long time to run' % str(size))
Guido van Rossum47f40342001-09-10 13:34:12 +0000235 else:
Christian Heimes77c02eb2008-02-09 02:18:51 +0000236 # Only run if the current filesystem supports large files.
237 # (Skip this test on Windows, since we now always support
238 # large files.)
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000239 f = open(TESTFN, 'wb', buffering=0)
Christian Heimes77c02eb2008-02-09 02:18:51 +0000240 try:
241 # 2**31 == 2147483648
242 f.seek(2147483649)
Serhiy Storchakac406a122013-07-17 13:42:24 +0300243 # Seeking is not enough of a test: you must write and flush, too!
Alexandre Vassalottia351f772008-03-03 02:59:49 +0000244 f.write(b'x')
Christian Heimes77c02eb2008-02-09 02:18:51 +0000245 f.flush()
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200246 except (OSError, OverflowError):
Serhiy Storchakac406a122013-07-17 13:42:24 +0300247 raise unittest.SkipTest("filesystem does not have "
248 "largefile support")
249 finally:
Christian Heimes77c02eb2008-02-09 02:18:51 +0000250 f.close()
251 unlink(TESTFN)
Serhiy Storchakac406a122013-07-17 13:42:24 +0300252
253
Giampaolo Rodola5bcc6d82019-09-30 12:51:55 +0800254class CLargeFileTest(TestFileMethods, unittest.TestCase):
Serhiy Storchakac406a122013-07-17 13:42:24 +0300255 open = staticmethod(io.open)
256
Giampaolo Rodola5bcc6d82019-09-30 12:51:55 +0800257
258class PyLargeFileTest(TestFileMethods, unittest.TestCase):
Serhiy Storchakac406a122013-07-17 13:42:24 +0300259 open = staticmethod(pyio.open)
260
Giampaolo Rodola5bcc6d82019-09-30 12:51:55 +0800261
Serhiy Storchakac406a122013-07-17 13:42:24 +0300262def tearDownModule():
263 unlink(TESTFN)
Giampaolo Rodola5bcc6d82019-09-30 12:51:55 +0800264 unlink(TESTFN2)
265
Guido van Rossum47f40342001-09-10 13:34:12 +0000266
Christian Heimes77c02eb2008-02-09 02:18:51 +0000267if __name__ == '__main__':
Serhiy Storchakac406a122013-07-17 13:42:24 +0300268 unittest.main()