|
4 | 4 | from test import support |
5 | 5 |
|
6 | 6 | import errno |
| 7 | +import io |
7 | 8 | import socket |
8 | 9 | import select |
9 | 10 | import _thread as thread |
@@ -906,6 +907,117 @@ def _testRealClose(self): |
906 | 907 | pass |
907 | 908 |
|
908 | 909 |
|
| 910 | +class FileObjectInterruptedTestCase(unittest.TestCase): |
| 911 | + """Test that the file object correctly handles EINTR internally.""" |
| 912 | + |
| 913 | + class MockSocket(object): |
| 914 | + def __init__(self, recv_funcs=()): |
| 915 | + # A generator that returns callables that we'll call for each |
| 916 | + # call to recv(). |
| 917 | + self._recv_step = iter(recv_funcs) |
| 918 | + |
| 919 | + def recv_into(self, buffer): |
| 920 | + data = next(self._recv_step)() |
| 921 | + assert len(buffer) >= len(data) |
| 922 | + buffer[:len(data)] = data |
| 923 | + return len(data) |
| 924 | + |
| 925 | + def _decref_socketios(self): |
| 926 | + pass |
| 927 | + |
| 928 | + def _textiowrap_for_test(self, buffering=-1): |
| 929 | + raw = socket.SocketIO(self, "r") |
| 930 | + if buffering < 0: |
| 931 | + buffering = io.DEFAULT_BUFFER_SIZE |
| 932 | + if buffering == 0: |
| 933 | + return raw |
| 934 | + buffer = io.BufferedReader(raw, buffering) |
| 935 | + text = io.TextIOWrapper(buffer, None, None) |
| 936 | + text.mode = "rb" |
| 937 | + return text |
| 938 | + |
| 939 | + @staticmethod |
| 940 | + def _raise_eintr(): |
| 941 | + raise socket.error(errno.EINTR) |
| 942 | + |
| 943 | + def _textiowrap_mock_socket(self, mock, buffering=-1): |
| 944 | + raw = socket.SocketIO(mock, "r") |
| 945 | + if buffering < 0: |
| 946 | + buffering = io.DEFAULT_BUFFER_SIZE |
| 947 | + if buffering == 0: |
| 948 | + return raw |
| 949 | + buffer = io.BufferedReader(raw, buffering) |
| 950 | + text = io.TextIOWrapper(buffer, None, None) |
| 951 | + text.mode = "rb" |
| 952 | + return text |
| 953 | + |
| 954 | + def _test_readline(self, size=-1, buffering=-1): |
| 955 | + mock_sock = self.MockSocket(recv_funcs=[ |
| 956 | + lambda : b"This is the first line\nAnd the sec", |
| 957 | + self._raise_eintr, |
| 958 | + lambda : b"ond line is here\n", |
| 959 | + lambda : b"", |
| 960 | + lambda : b"", # XXX(gps): io library does an extra EOF read |
| 961 | + ]) |
| 962 | + fo = mock_sock._textiowrap_for_test(buffering=buffering) |
| 963 | + self.assertEquals(fo.readline(size), "This is the first line\n") |
| 964 | + self.assertEquals(fo.readline(size), "And the second line is here\n") |
| 965 | + |
| 966 | + def _test_read(self, size=-1, buffering=-1): |
| 967 | + mock_sock = self.MockSocket(recv_funcs=[ |
| 968 | + lambda : b"This is the first line\nAnd the sec", |
| 969 | + self._raise_eintr, |
| 970 | + lambda : b"ond line is here\n", |
| 971 | + lambda : b"", |
| 972 | + lambda : b"", # XXX(gps): io library does an extra EOF read |
| 973 | + ]) |
| 974 | + expecting = (b"This is the first line\n" |
| 975 | + b"And the second line is here\n") |
| 976 | + fo = mock_sock._textiowrap_for_test(buffering=buffering) |
| 977 | + if buffering == 0: |
| 978 | + data = b'' |
| 979 | + else: |
| 980 | + data = '' |
| 981 | + expecting = expecting.decode('utf8') |
| 982 | + while len(data) != len(expecting): |
| 983 | + part = fo.read(size) |
| 984 | + if not part: |
| 985 | + break |
| 986 | + data += part |
| 987 | + self.assertEquals(data, expecting) |
| 988 | + |
| 989 | + def test_default(self): |
| 990 | + self._test_readline() |
| 991 | + self._test_readline(size=100) |
| 992 | + self._test_read() |
| 993 | + self._test_read(size=100) |
| 994 | + |
| 995 | + def test_with_1k_buffer(self): |
| 996 | + self._test_readline(buffering=1024) |
| 997 | + self._test_readline(size=100, buffering=1024) |
| 998 | + self._test_read(buffering=1024) |
| 999 | + self._test_read(size=100, buffering=1024) |
| 1000 | + |
| 1001 | + def _test_readline_no_buffer(self, size=-1): |
| 1002 | + mock_sock = self.MockSocket(recv_funcs=[ |
| 1003 | + lambda : b"a", |
| 1004 | + lambda : b"\n", |
| 1005 | + lambda : b"B", |
| 1006 | + self._raise_eintr, |
| 1007 | + lambda : b"b", |
| 1008 | + lambda : b"", |
| 1009 | + ]) |
| 1010 | + fo = mock_sock._textiowrap_for_test(buffering=0) |
| 1011 | + self.assertEquals(fo.readline(size), b"a\n") |
| 1012 | + self.assertEquals(fo.readline(size), b"Bb") |
| 1013 | + |
| 1014 | + def test_no_buffer(self): |
| 1015 | + self._test_readline_no_buffer() |
| 1016 | + self._test_readline_no_buffer(size=4) |
| 1017 | + self._test_read(buffering=0) |
| 1018 | + self._test_read(size=100, buffering=0) |
| 1019 | + |
| 1020 | + |
909 | 1021 | class UnbufferedFileObjectClassTestCase(FileObjectClassTestCase): |
910 | 1022 |
|
911 | 1023 | """Repeat the tests from FileObjectClassTestCase with bufsize==0. |
@@ -1310,6 +1422,7 @@ def test_main(): |
1310 | 1422 | tests.extend([ |
1311 | 1423 | NonBlockingTCPTests, |
1312 | 1424 | FileObjectClassTestCase, |
| 1425 | + FileObjectInterruptedTestCase, |
1313 | 1426 | UnbufferedFileObjectClassTestCase, |
1314 | 1427 | LineBufferedFileObjectClassTestCase, |
1315 | 1428 | SmallBufferedFileObjectClassTestCase, |
|
0 commit comments