From bb70322bf5ae5cf9ed7b5cf41ec0e0b92b66d643 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 5 Oct 2023 14:24:56 +0300 Subject: [PATCH 1/3] gh-110388: Add tests for tty --- Lib/test/test_tty.py | 78 +++++++++++++++++++ ...-10-05-14-22-48.gh-issue-110388.1-HQJO.rst | 1 + 2 files changed, 79 insertions(+) create mode 100644 Lib/test/test_tty.py create mode 100644 Misc/NEWS.d/next/Tests/2023-10-05-14-22-48.gh-issue-110388.1-HQJO.rst diff --git a/Lib/test/test_tty.py b/Lib/test/test_tty.py new file mode 100644 index 00000000000000..cd3e589ecb33d3 --- /dev/null +++ b/Lib/test/test_tty.py @@ -0,0 +1,78 @@ +import io +import unittest +from test.support.import_helper import import_module + +termios = import_module('termios') +tty = import_module('tty') + + +class TestTty(unittest.TestCase): + + def setUp(self): + self.stream = open('/dev/tty', 'wb', buffering=0) + self.addCleanup(self.stream.close) + self.fd = self.stream.fileno() + self.mode = termios.tcgetattr(self.fd) + self.addCleanup(termios.tcsetattr, self.fd, termios.TCSANOW, self.mode) + self.addCleanup(termios.tcsetattr, self.fd, termios.TCSAFLUSH, self.mode) + + def check_cbreak(self, mode): + self.assertEqual(mode[0] & termios.ICRNL, 0) + self.assertEqual(mode[3] & termios.ECHO, 0) + self.assertEqual(mode[3] & termios.ICANON, 0) + self.assertEqual(mode[6][termios.VMIN], 1) + self.assertEqual(mode[6][termios.VTIME], 0) + + def check_raw(self, mode): + self.check_cbreak(mode) + self.assertEqual(mode[0] & termios.ISTRIP, 0) + self.assertEqual(mode[0] & termios.ICRNL, 0) + self.assertEqual(mode[1] & termios.OPOST, 0) + self.assertEqual(mode[2] & termios.PARENB, termios.CS8 & termios.PARENB) + self.assertEqual(mode[2] & termios.CSIZE, termios.CS8 & termios.CSIZE) + self.assertEqual(mode[2] & termios.CS8, termios.CS8) + self.assertEqual(mode[3] & termios.ECHO, 0) + self.assertEqual(mode[3] & termios.ICANON, 0) + self.assertEqual(mode[3] & termios.ISIG, 0) + self.assertEqual(mode[6][termios.VMIN], 1) + self.assertEqual(mode[6][termios.VTIME], 0) + + def test_cfmakeraw(self): + mode = termios.tcgetattr(self.fd) + self.assertEqual(mode, self.mode) + tty.cfmakeraw(mode) + self.check_raw(mode) + self.assertEqual(mode[4], self.mode[4]) + self.assertEqual(mode[5], self.mode[5]) + + def test_cfmakecbreak(self): + mode = termios.tcgetattr(self.fd) + self.assertEqual(mode, self.mode) + tty.cfmakecbreak(mode) + self.check_cbreak(mode) + self.assertEqual(mode[1], self.mode[1]) + self.assertEqual(mode[2], self.mode[2]) + self.assertEqual(mode[4], self.mode[4]) + self.assertEqual(mode[5], self.mode[5]) + + def test_setraw(self): + mode = tty.setraw(self.fd) + mode2 = termios.tcgetattr(self.fd) + self.check_raw(mode2) + mode3 = tty.setraw(self.fd, termios.TCSANOW) + self.assertEqual(mode3, mode2) + tty.setraw(self.stream) + tty.setraw(fd=self.fd, when=termios.TCSANOW) + + def test_setcbreak(self): + mode = tty.setcbreak(self.fd) + mode2 = termios.tcgetattr(self.fd) + self.check_cbreak(mode2) + mode3 = tty.setcbreak(self.fd, termios.TCSANOW) + self.assertEqual(mode3, mode2) + tty.setcbreak(self.stream) + tty.setcbreak(fd=self.fd, when=termios.TCSANOW) + + +if __name__ == '__main__': + unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2023-10-05-14-22-48.gh-issue-110388.1-HQJO.rst b/Misc/NEWS.d/next/Tests/2023-10-05-14-22-48.gh-issue-110388.1-HQJO.rst new file mode 100644 index 00000000000000..caac41f81547de --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-05-14-22-48.gh-issue-110388.1-HQJO.rst @@ -0,0 +1 @@ +Add tests for :mod:`tty`. From 80b3f21c38d2ac6b9bae22fb453453ac4d6104bf Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 5 Oct 2023 15:33:04 +0300 Subject: [PATCH 2/3] Skip tests if /dev/tty is not available. --- Lib/test/test_tty.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_tty.py b/Lib/test/test_tty.py index cd3e589ecb33d3..846fbb833a4af7 100644 --- a/Lib/test/test_tty.py +++ b/Lib/test/test_tty.py @@ -9,7 +9,10 @@ class TestTty(unittest.TestCase): def setUp(self): - self.stream = open('/dev/tty', 'wb', buffering=0) + try: + self.stream = open('/dev/tty', 'wb', buffering=0) + except OSError: + self.skipTest("Cannot open '/dev/tty'") self.addCleanup(self.stream.close) self.fd = self.stream.fileno() self.mode = termios.tcgetattr(self.fd) From 99858f0e307895da17386187df6dd8610a6df7a5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 6 Oct 2023 14:48:22 +0300 Subject: [PATCH 3/3] Use pseudo-terminal. --- Lib/test/test_tty.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_tty.py b/Lib/test/test_tty.py index 846fbb833a4af7..6993047492b5ec 100644 --- a/Lib/test/test_tty.py +++ b/Lib/test/test_tty.py @@ -1,4 +1,4 @@ -import io +import os import unittest from test.support.import_helper import import_module @@ -6,14 +6,13 @@ tty = import_module('tty') +@unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()") class TestTty(unittest.TestCase): def setUp(self): - try: - self.stream = open('/dev/tty', 'wb', buffering=0) - except OSError: - self.skipTest("Cannot open '/dev/tty'") - self.addCleanup(self.stream.close) + master_fd, self.fd = os.openpty() + self.addCleanup(os.close, master_fd) + self.stream = self.enterContext(open(self.fd, 'wb', buffering=0)) self.fd = self.stream.fileno() self.mode = termios.tcgetattr(self.fd) self.addCleanup(termios.tcsetattr, self.fd, termios.TCSANOW, self.mode)