|
5 | 5 | import stat
|
6 | 6 | import sys
|
7 | 7 | 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 |
9 | 12 | import io # C implementation of io
|
10 | 13 | import _pyio as pyio # Python implementation of io
|
11 | 14 |
|
12 | 15 | # size of file to create (>2 GiB; 2 GiB == 2,147,483,648 bytes)
|
13 | 16 | size = 2_500_000_000
|
| 17 | +TESTFN2 = TESTFN + '2' |
| 18 | + |
14 | 19 |
|
15 | 20 | class LargeFileTest:
|
16 |
| - """Test that each file function works as expected for large |
17 |
| - (i.e. > 2 GiB) files. |
18 |
| - """ |
19 | 21 |
|
20 | 22 | def setUp(self):
|
21 | 23 | if os.path.exists(TESTFN):
|
@@ -44,6 +46,13 @@ def tearDownClass(cls):
|
44 | 46 | if not os.stat(TESTFN)[stat.ST_SIZE] == 0:
|
45 | 47 | raise cls.failureException('File was not truncated by opening '
|
46 | 48 | '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 | + """ |
47 | 56 |
|
48 | 57 | # _pyio.FileIO.readall() uses a temporary bytearray then casted to bytes,
|
49 | 58 | # so memuse=2 is needed
|
@@ -140,6 +149,72 @@ def test_seekable(self):
|
140 | 149 | f.seek(pos)
|
141 | 150 | self.assertTrue(f.seekable())
|
142 | 151 |
|
| 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 | + |
143 | 218 | def setUpModule():
|
144 | 219 | try:
|
145 | 220 | import signal
|
@@ -176,14 +251,18 @@ def setUpModule():
|
176 | 251 | unlink(TESTFN)
|
177 | 252 |
|
178 | 253 |
|
179 |
| -class CLargeFileTest(LargeFileTest, unittest.TestCase): |
| 254 | +class CLargeFileTest(TestFileMethods, unittest.TestCase): |
180 | 255 | open = staticmethod(io.open)
|
181 | 256 |
|
182 |
| -class PyLargeFileTest(LargeFileTest, unittest.TestCase): |
| 257 | + |
| 258 | +class PyLargeFileTest(TestFileMethods, unittest.TestCase): |
183 | 259 | open = staticmethod(pyio.open)
|
184 | 260 |
|
| 261 | + |
185 | 262 | def tearDownModule():
|
186 | 263 | unlink(TESTFN)
|
| 264 | + unlink(TESTFN2) |
| 265 | + |
187 | 266 |
|
188 | 267 | if __name__ == '__main__':
|
189 | 268 | unittest.main()
|
0 commit comments