Commit e44dba3b authored by Martin Panter's avatar Martin Panter

Issue #24402: Factor out PtyTests.run_child() in input() tests

This reuses existing code to hopefully make the new test_input_no_stdout_
fileno() test work. It is hanging Free BSD 9 and OS X Tiger buildbots, and I
don't know why.
parent c9a6ab56
...@@ -1493,21 +1493,14 @@ class PtyTests(unittest.TestCase): ...@@ -1493,21 +1493,14 @@ class PtyTests(unittest.TestCase):
"""Tests that use a pseudo terminal to guarantee stdin and stdout are """Tests that use a pseudo terminal to guarantee stdin and stdout are
terminals in the test environment""" terminals in the test environment"""
def fork(self): def run_child(self, child, terminal_input):
r, w = os.pipe() # Pipe test results from child back to parent
try: try:
return pty.fork() pid, fd = pty.fork()
except (OSError, AttributeError) as e: except (OSError, AttributeError) as e:
self.skipTest("pty.fork() raised {}".format(e))
def check_input_tty(self, prompt, terminal_input, stdio_encoding=None):
if not sys.stdin.isatty() or not sys.stdout.isatty():
self.skipTest("stdin and stdout must be ttys")
r, w = os.pipe()
try:
pid, fd = self.fork()
except:
os.close(r) os.close(r)
os.close(w) os.close(w)
self.skipTest("pty.fork() raised {}".format(e))
raise raise
if pid == 0: if pid == 0:
# Child # Child
...@@ -1515,17 +1508,8 @@ class PtyTests(unittest.TestCase): ...@@ -1515,17 +1508,8 @@ class PtyTests(unittest.TestCase):
# Make sure we don't get stuck if there's a problem # Make sure we don't get stuck if there's a problem
signal.alarm(2) signal.alarm(2)
os.close(r) os.close(r)
# Check the error handlers are accounted for
if stdio_encoding:
sys.stdin = io.TextIOWrapper(sys.stdin.detach(),
encoding=stdio_encoding,
errors='surrogateescape')
sys.stdout = io.TextIOWrapper(sys.stdout.detach(),
encoding=stdio_encoding,
errors='replace')
with open(w, "w") as wpipe: with open(w, "w") as wpipe:
print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe) child(wpipe)
print(ascii(input(prompt)), file=wpipe)
except: except:
traceback.print_exc() traceback.print_exc()
finally: finally:
...@@ -1533,7 +1517,7 @@ class PtyTests(unittest.TestCase): ...@@ -1533,7 +1517,7 @@ class PtyTests(unittest.TestCase):
os._exit(0) os._exit(0)
# Parent # Parent
os.close(w) os.close(w)
os.write(fd, terminal_input + b"\r\n") os.write(fd, terminal_input)
# Get results from the pipe # Get results from the pipe
with open(r, "r") as rpipe: with open(r, "r") as rpipe:
lines = [] lines = []
...@@ -1546,10 +1530,38 @@ class PtyTests(unittest.TestCase): ...@@ -1546,10 +1530,38 @@ class PtyTests(unittest.TestCase):
# Check the result was got and corresponds to the user's terminal input # Check the result was got and corresponds to the user's terminal input
if len(lines) != 2: if len(lines) != 2:
# Something went wrong, try to get at stderr # Something went wrong, try to get at stderr
with open(fd, "r", encoding="ascii", errors="ignore") as child_output: # Beware of Linux raising EIO when the slave is closed
child_output = bytearray()
while True:
try:
chunk = os.read(fd, 3000)
except OSError: # Assume EIO
break
if not chunk:
break
child_output.extend(chunk)
os.close(fd)
child_output = child_output.decode("ascii", "ignore")
self.fail("got %d lines in pipe but expected 2, child output was:\n%s" self.fail("got %d lines in pipe but expected 2, child output was:\n%s"
% (len(lines), child_output.read())) % (len(lines), child_output))
os.close(fd) os.close(fd)
return lines
def check_input_tty(self, prompt, terminal_input, stdio_encoding=None):
if not sys.stdin.isatty() or not sys.stdout.isatty():
self.skipTest("stdin and stdout must be ttys")
def child(wpipe):
# Check the error handlers are accounted for
if stdio_encoding:
sys.stdin = io.TextIOWrapper(sys.stdin.detach(),
encoding=stdio_encoding,
errors='surrogateescape')
sys.stdout = io.TextIOWrapper(sys.stdout.detach(),
encoding=stdio_encoding,
errors='replace')
print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe)
print(ascii(input(prompt)), file=wpipe)
lines = self.run_child(child, terminal_input + b"\r\n")
# Check we did exercise the GNU readline path # Check we did exercise the GNU readline path
self.assertIn(lines[0], {'tty = True', 'tty = False'}) self.assertIn(lines[0], {'tty = True', 'tty = False'})
if lines[0] != 'tty = True': if lines[0] != 'tty = True':
...@@ -1577,26 +1589,17 @@ class PtyTests(unittest.TestCase): ...@@ -1577,26 +1589,17 @@ class PtyTests(unittest.TestCase):
def test_input_no_stdout_fileno(self): def test_input_no_stdout_fileno(self):
# Issue #24402: If stdin is the original terminal but stdout.fileno() # Issue #24402: If stdin is the original terminal but stdout.fileno()
# fails, do not use the original stdout file descriptor # fails, do not use the original stdout file descriptor
pid, pty = self.fork() def child(wpipe):
if pid: # Parent process print("stdin.isatty():", sys.stdin.isatty(), file=wpipe)
# Ideally this should read and write concurrently using select()
# or similar, to avoid the possibility of a deadlock.
os.write(pty, b"quux\r")
_, status = os.waitpid(pid, 0)
output = os.read(pty, 3000).decode("ascii", "backslashreplace")
os.close(pty)
self.assertEqual(status, 0, output)
else: # Child process
try:
self.assertTrue(sys.stdin.isatty(), "stdin not a terminal")
sys.stdout = io.StringIO() # Does not support fileno() sys.stdout = io.StringIO() # Does not support fileno()
input("prompt") input("prompt")
self.assertEqual(sys.stdout.getvalue(), "prompt") print("captured:", ascii(sys.stdout.getvalue()), file=wpipe)
os._exit(0) # Success! lines = self.run_child(child, b"quux\r")
except: expected = (
sys.excepthook(*sys.exc_info()) "stdin.isatty(): True",
finally: "captured: 'prompt'",
os._exit(1) # Failure )
self.assertSequenceEqual(lines, expected)
class TestSorted(unittest.TestCase): class TestSorted(unittest.TestCase):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment