bpo-37096: Add large-file tests for modules using sendfile(2) (GH-13676) · python/cpython@5bcc6d8
@@ -5,17 +5,19 @@
55import stat
66import sys
77import 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
912import io # C implementation of io
1013import _pyio as pyio # Python implementation of io
11141215# size of file to create (>2 GiB; 2 GiB == 2,147,483,648 bytes)
1316size = 2_500_000_000
17+TESTFN2 = TESTFN + '2'
18+14191520class LargeFileTest:
16-"""Test that each file function works as expected for large
17- (i.e. > 2 GiB) files.
18- """
19212022def setUp(self):
2123if os.path.exists(TESTFN):
@@ -44,6 +46,13 @@ def tearDownClass(cls):
4446if not os.stat(TESTFN)[stat.ST_SIZE] == 0:
4547raise 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+ """
47564857# _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):
140149f.seek(pos)
141150self.assertTrue(f.seekable())
142151152+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+143218def setUpModule():
144219try:
145220import signal
@@ -176,14 +251,18 @@ def setUpModule():
176251unlink(TESTFN)
177252178253179-class CLargeFileTest(LargeFileTest, unittest.TestCase):
254+class CLargeFileTest(TestFileMethods, unittest.TestCase):
180255open = staticmethod(io.open)
181256182-class PyLargeFileTest(LargeFileTest, unittest.TestCase):
257+258+class PyLargeFileTest(TestFileMethods, unittest.TestCase):
183259open = staticmethod(pyio.open)
184260261+185262def tearDownModule():
186263unlink(TESTFN)
264+unlink(TESTFN2)
265+187266188267if __name__ == '__main__':
189268unittest.main()