From 5ad0e356e04ae88eed4c80d78e01891b6ef1d408 Mon Sep 17 00:00:00 2001 From: diekmann Date: Thu, 28 Sep 2017 18:28:55 +0200 Subject: [PATCH 1/3] bpo-31158: Fix nondeterministic read in test_pty --- Lib/test/test_pty.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index f283e1930b7ebc4..1d3e0f788d45c21 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -23,6 +23,16 @@ def debug(msg): pass +# Note that os.read() is nondeterministic so we need to be very careful +# to make the test suite deterministic. A normal call to os.read() may +# give us less than expected. +# +# Beware, on my Linux system, if I put 'foo\n' into a terminal fd, I get +# back 'foo\r\n' at the other end. The behavior depends on the termios +# setting. The newline translation may be OS-specific. To make the +# test suite deterministic and OS-independent, _os_readline and +# normalize_output can be used. + def normalize_output(data): # Some operating systems do conversions on newline. We could possibly fix # that by doing the appropriate termios.tcsetattr()s. I couldn't figure out @@ -43,6 +53,19 @@ def normalize_output(data): return data +def _os_readline(fd): + """Use os.read() to read byte by byte until a newline is + encountered. May block forever if no newline is read.""" + buf = [] + while True: + r = os.read(fd, 1) + if not r: + raise EOFError + buf.append(r) + if r == b'\n': + break + return b''.join(buf) + # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. @@ -94,14 +117,14 @@ def test_basic(self): debug("Writing to slave_fd") os.write(slave_fd, TEST_STRING_1) - s1 = os.read(master_fd, 1024) + s1 = _os_readline(master_fd) self.assertEqual(b'I wish to buy a fish license.\n', normalize_output(s1)) debug("Writing chunked output") os.write(slave_fd, TEST_STRING_2[:5]) os.write(slave_fd, TEST_STRING_2[5:]) - s2 = os.read(master_fd, 1024) + s2 = _os_readline(master_fd) self.assertEqual(b'For my pet fish, Eric.\n', normalize_output(s2)) os.close(slave_fd) From 29ca53abef5383e4e64a337fe279bebed36f9526 Mon Sep 17 00:00:00 2001 From: diekmann Date: Thu, 28 Sep 2017 20:10:50 +0200 Subject: [PATCH 2/3] Reuse existing readline implementation from io. Thx to @pitrou --- Lib/test/test_pty.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index 1d3e0f788d45c21..6a65507d1e0e569 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -10,6 +10,7 @@ import select import signal import socket +import io # readline import unittest TEST_STRING_1 = b"I wish to buy a fish license.\n" @@ -53,18 +54,11 @@ def normalize_output(data): return data -def _os_readline(fd): - """Use os.read() to read byte by byte until a newline is - encountered. May block forever if no newline is read.""" - buf = [] - while True: - r = os.read(fd, 1) - if not r: - raise EOFError - buf.append(r) - if r == b'\n': - break - return b''.join(buf) +def _readline(fd): + """Read one line. May block forever if no newline is read.""" + reader = io.FileIO(fd, mode='rb', closefd=False) + return reader.readline() + # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing @@ -117,14 +111,14 @@ def test_basic(self): debug("Writing to slave_fd") os.write(slave_fd, TEST_STRING_1) - s1 = _os_readline(master_fd) + s1 = _readline(master_fd) self.assertEqual(b'I wish to buy a fish license.\n', normalize_output(s1)) debug("Writing chunked output") os.write(slave_fd, TEST_STRING_2[:5]) os.write(slave_fd, TEST_STRING_2[5:]) - s2 = _os_readline(master_fd) + s2 = _readline(master_fd) self.assertEqual(b'For my pet fish, Eric.\n', normalize_output(s2)) os.close(slave_fd) From 4f8137bb7ae41922ea8698a98c87823897eb48f5 Mon Sep 17 00:00:00 2001 From: diekmann Date: Thu, 28 Sep 2017 21:48:53 +0200 Subject: [PATCH 3/3] Updated comment Ideally, this commit is fixuped into the previous commit. Since there is already a comment on github, I won't rebase. --- Lib/test/test_pty.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index 6a65507d1e0e569..3b448569a2ffcb9 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -31,8 +31,8 @@ def debug(msg): # Beware, on my Linux system, if I put 'foo\n' into a terminal fd, I get # back 'foo\r\n' at the other end. The behavior depends on the termios # setting. The newline translation may be OS-specific. To make the -# test suite deterministic and OS-independent, _os_readline and -# normalize_output can be used. +# test suite deterministic and OS-independent, the functions _readline +# and normalize_output can be used. def normalize_output(data): # Some operating systems do conversions on newline. We could possibly fix