FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

bpo-41818: Updated tests for the standard pty library by 8vasu · Pull Request #22962 · python/cpython · GitHub

Repository navigation

Next Next commit
Updated tests for the pty standard library
Signed-off-by: Soumendra Ganguly <soumendraganguly@gmail.com>
  • Loading branch information
8vasu committed Oct 25, 2020
commit 323fc9c9d07c6a87e6453ff8ea0b083ec319d804
104 changes: 93 additions & 11 deletions Lib/test/test_pty.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import_module('termios')

import errno
# try:
Comment thread
8vasu marked this conversation as resolved.
Outdated
# import pty2 as pty
# except ModuleNotFoundError:
# import pty
import pty
import os
import sys
Expand All @@ -14,6 +18,11 @@
import io # readline
import unittest

import struct
import tty
import fcntl
import platform

TEST_STRING_1 = b"I wish to buy a fish license.\n"
TEST_STRING_2 = b"For my pet fish, Eric.\n"

Expand Down Expand Up @@ -60,11 +69,16 @@ def _readline(fd):
reader = io.FileIO(fd, mode='rb', closefd=False)
return reader.readline()

def expectedFailureOnBSD(fun):
if platform.system().endswith("BSD"):
return unittest.expectedFailure(fun)
Comment thread
ethanfurman marked this conversation as resolved.
Outdated
return fun


# Marginal testing of pty suite. Cannot do extensive 'do or fail' testing
# because pty code is not too portable.
# XXX(nnorwitz): these tests leak fds when there is an error.
# Soumendra: test_openpty may leave tty in abnormal state upon failure.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Could we potentially surround with a try...finally to avoid this? A single test failure should really not result in the other tests within test_pty being unreliable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Good point. Thank you. I will work on this.

class PtyTest(unittest.TestCase):
def setUp(self):
old_alarm = signal.signal(signal.SIGALRM, self.handle_sig)
Expand All @@ -81,28 +95,74 @@ def setUp(self):
def handle_sig(self, sig, frame):
self.fail("isatty hung")

# RELEVANT ANYMORE?
@staticmethod
def handle_sighup(signum, frame):
# bpo-38547: if the process is the session leader, os.close(master_fd)
# of "master_fd, slave_name = pty.master_open()" raises SIGHUP
# signal: just ignore the signal.
pass

def test_basic(self):
@unittest.expectedFailure
def test_openpty(self):
try:
mode = tty.tcgetattr(pty.STDIN_FILENO)
except tty.error:
# pty.STDIN_FILENO not a tty?
debug("tty.tcgetattr(pty.STDIN_FILENO) failed")
mode = None

try:
debug("Calling master_open()")
master_fd, slave_name = pty.master_open()
debug("Got master_fd '%d', slave_name '%s'" %
(master_fd, slave_name))
debug("Calling slave_open(%r)" % (slave_name,))
slave_fd = pty.slave_open(slave_name)
debug("Got slave_fd '%d'" % slave_fd)
TIOCGWINSZ = tty.TIOCGWINSZ
TIOCSWINSZ = tty.TIOCSWINSZ
except AttributeError:
debug("TIOCSWINSZ/TIOCGWINSZ not available")
winsz = None
else:
try:
debug("Setting pty.STDIN_FILENO window size")
current_stdin_winsz = os.get_terminal_size(pty.STDIN_FILENO)

# Set number of columns and rows to be the
# floors of 1/5 of respective original values
winsz = struct.pack("HHHH", current_stdin_winsz.lines//5,
current_stdin_winsz.columns//5, 0, 0)
fcntl.ioctl(pty.STDIN_FILENO, TIOCSWINSZ, winsz)

# Were we able to set the window size
# of pty.STDIN_FILENO successfully?
s = struct.pack("HHHH", 0, 0, 0, 0)
new_stdin_winsz = fcntl.ioctl(pty.STDIN_FILENO, TIOCGWINSZ, s)
self.assertEqual(new_stdin_winsz, winsz,
"pty.STDIN_FILENO window size unchanged")
except OSError:
# pty.STDIN_FILENO not a tty?
debug("Failed to set pty.STDIN_FILENO window size")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Shouldn't this at least result in a warning via warnings.warn()? I suspect it would get buried otherwise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

You are absolutely correct. I will change this. I have one question before I change this. If I use warnings.warn() here, then it will always generate warnings when the test is not run with stdin being a tty; for example the automated tests done by GitHub. Is that going to be an issue?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

It may cause a slight bit of extra noise on the tests, but it shouldn't cause the GitHub CI to fail when the warning is emitted (I don't believe we have any of our CI set to fail on warning, but it can be ran with that setting locally).

winsz = None

try:
debug("Calling pty.openpty()")
try:
master_fd, slave_fd = pty.openpty(mode, winsz)
except TypeError:
master_fd, slave_fd = pty.openpty()
debug("Got master_fd '%d', slave_fd '%d'" %
(master_fd, slave_fd))
except OSError:
# " An optional feature could not be imported " ... ?
raise unittest.SkipTest("Pseudo-terminals (seemingly) not functional.")

self.assertTrue(os.isatty(slave_fd), 'slave_fd is not a tty')
self.assertTrue(os.isatty(slave_fd), "slave_fd is not a tty")

if mode:
self.assertEqual(tty.tcgetattr(slave_fd), mode,
"openpty() failed to set slave termios")
if winsz:
s = struct.pack("HHHH", 0, 0, 0, 0)
self.assertEqual(fcntl.ioctl(slave_fd, TIOCGWINSZ, s), winsz,
"openpty() failed to set slave window size")

# RELEVANT ANYMORE?
# Solaris requires reading the fd before anything is returned.
# My guess is that since we open and close the slave fd
# in master_open(), we need to read the EOF.
Expand Down Expand Up @@ -139,6 +199,13 @@ def test_basic(self):
# to ignore this signal.
os.close(master_fd)

if winsz:
winsz = struct.pack("HHHH", current_stdin_winsz.lines,
current_stdin_winsz.columns, 0, 0)
fcntl.ioctl(pty.STDIN_FILENO, TIOCSWINSZ, winsz)

# pty.openpty() passed.

def test_fork(self):
debug("calling pty.fork()")
pid, master_fd = pty.fork()
Expand Down Expand Up @@ -224,6 +291,21 @@ def test_fork(self):

# pty.fork() passed.

@expectedFailureOnBSD
def test_master_read(self):
debug("Calling pty.openpty()")
master_fd, slave_fd = pty.openpty()
debug("Got master_fd '%d', slave_fd '%d'" %
(master_fd, slave_fd))

debug("Closing slave_fd")
os.close(slave_fd)

debug("Reading from master_fd")
with self.assertRaises(OSError):
os.read(master_fd, 1)

os.close(master_fd)
Comment thread
ethanfurman marked this conversation as resolved.
Outdated

class SmallPtyTests(unittest.TestCase):
"""These tests don't spawn children or hang."""
Expand Down Expand Up @@ -262,8 +344,9 @@ def _socketpair(self):
self.files.extend(socketpair)
return socketpair

def _mock_select(self, rfds, wfds, xfds):
def _mock_select(self, rfds, wfds, xfds, timeout=0):
Comment thread
ethanfurman marked this conversation as resolved.
Outdated
# This will raise IndexError when no more expected calls exist.
# This ignores the timeout
self.assertEqual(self.select_rfds_lengths.pop(0), len(rfds))
return self.select_rfds_results.pop(0), [], []

Expand Down Expand Up @@ -318,7 +401,6 @@ def test__copy_eof_on_all(self):
with self.assertRaises(IndexError):
pty._copy(masters[0])


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This existing whitespace line should be re-added, since generally there are two lines in between the last line of a class's body and functions after it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Thank you for teaching me this. I will make this change.

def tearDownModule():
reap_children()

Expand Down

Back | FazBrowse Home | New Git URL