Skip to content

Commit 5bcc6d8

Browse files
authored
bpo-37096: Add large-file tests for modules using sendfile(2) (GH-13676)
1 parent 25e115e commit 5bcc6d8

File tree

1 file changed

+85
-6
lines changed

1 file changed

+85
-6
lines changed

Lib/test/test_largefile.py

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@
55
import stat
66
import sys
77
import unittest
8-
from test.support import TESTFN, requires, unlink, bigmemtest
8+
import socket
9+
import shutil
10+
import threading
11+
from test.support import TESTFN, requires, unlink, bigmemtest, find_unused_port
912
import io # C implementation of io
1013
import _pyio as pyio # Python implementation of io
1114

1215
# size of file to create (>2 GiB; 2 GiB == 2,147,483,648 bytes)
1316
size = 2_500_000_000
17+
TESTFN2 = TESTFN + '2'
18+
1419

1520
class LargeFileTest:
16-
"""Test that each file function works as expected for large
17-
(i.e. > 2 GiB) files.
18-
"""
1921

2022
def setUp(self):
2123
if os.path.exists(TESTFN):
@@ -44,6 +46,13 @@ def tearDownClass(cls):
4446
if not os.stat(TESTFN)[stat.ST_SIZE] == 0:
4547
raise cls.failureException('File was not truncated by opening '
4648
'with mode "wb"')
49+
unlink(TESTFN2)
50+
51+
52+
class TestFileMethods(LargeFileTest):
53+
"""Test that each file function works as expected for large
54+
(i.e. > 2 GiB) files.
55+
"""
4756

4857
# _pyio.FileIO.readall() uses a temporary bytearray then casted to bytes,
4958
# so memuse=2 is needed
@@ -140,6 +149,72 @@ def test_seekable(self):
140149
f.seek(pos)
141150
self.assertTrue(f.seekable())
142151

152+
153+
class 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')
169+
class 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+
143218
def setUpModule():
144219
try:
145220
import signal
@@ -176,14 +251,18 @@ def setUpModule():
176251
unlink(TESTFN)
177252

178253

179-
class CLargeFileTest(LargeFileTest, unittest.TestCase):
254+
class CLargeFileTest(TestFileMethods, unittest.TestCase):
180255
open = staticmethod(io.open)
181256

182-
class PyLargeFileTest(LargeFileTest, unittest.TestCase):
257+
258+
class PyLargeFileTest(TestFileMethods, unittest.TestCase):
183259
open = staticmethod(pyio.open)
184260

261+
185262
def tearDownModule():
186263
unlink(TESTFN)
264+
unlink(TESTFN2)
265+
187266

188267
if __name__ == '__main__':
189268
unittest.main()

0 commit comments

Comments
 (0)