Skip to content

Commit 0d53872

Browse files
[3.13] gh-132742: Improve tests for fcntl.ioctl() (GH-132791) (GH-133066)
* Use better tests for integer argument. * Add also parallel tests for tcflush() and tcflow(). (cherry picked from commit ed8e886)
1 parent 154225b commit 0d53872

File tree

2 files changed

+104
-7
lines changed

2 files changed

+104
-7
lines changed

Lib/test/test_ioctl.py

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import array
22
import os
33
import struct
4+
import sys
45
import threading
56
import unittest
67
from test.support import get_attribute
@@ -139,11 +140,55 @@ def setUp(self):
139140
self.addCleanup(os.close, self.master_fd)
140141

141142
@unittest.skipUnless(hasattr(termios, 'TCFLSH'), 'requires termios.TCFLSH')
142-
def test_ioctl_tcflush(self):
143-
r = fcntl.ioctl(self.slave_fd, termios.TCFLSH, termios.TCIFLUSH)
144-
self.assertEqual(r, 0)
145-
r = fcntl.ioctl(self.slave_fd, termios.TCFLSH, termios.TCOFLUSH)
146-
self.assertEqual(r, 0)
143+
def test_ioctl_clear_input_or_output(self):
144+
wfd = self.slave_fd
145+
rfd = self.master_fd
146+
inbuf = sys.platform == 'linux'
147+
148+
os.write(wfd, b'abcdef')
149+
self.assertEqual(os.read(rfd, 2), b'ab')
150+
if inbuf:
151+
# don't flush input
152+
fcntl.ioctl(rfd, termios.TCFLSH, termios.TCOFLUSH)
153+
else:
154+
# don't flush output
155+
fcntl.ioctl(wfd, termios.TCFLSH, termios.TCIFLUSH)
156+
self.assertEqual(os.read(rfd, 2), b'cd')
157+
if inbuf:
158+
# flush input
159+
fcntl.ioctl(rfd, termios.TCFLSH, termios.TCIFLUSH)
160+
else:
161+
# flush output
162+
fcntl.ioctl(wfd, termios.TCFLSH, termios.TCOFLUSH)
163+
os.write(wfd, b'ABCDEF')
164+
self.assertEqual(os.read(rfd, 1024), b'ABCDEF')
165+
166+
@unittest.skipUnless(sys.platform == 'linux', 'only works on Linux')
167+
@unittest.skipUnless(hasattr(termios, 'TCXONC'), 'requires termios.TCXONC')
168+
def test_ioctl_suspend_and_resume_output(self):
169+
wfd = self.slave_fd
170+
rfd = self.master_fd
171+
write_suspended = threading.Event()
172+
write_finished = threading.Event()
173+
174+
def writer():
175+
os.write(wfd, b'abc')
176+
write_suspended.wait()
177+
os.write(wfd, b'def')
178+
write_finished.set()
179+
180+
with threading_helper.start_threads([threading.Thread(target=writer)]):
181+
self.assertEqual(os.read(rfd, 3), b'abc')
182+
try:
183+
fcntl.ioctl(wfd, termios.TCXONC, termios.TCOOFF)
184+
write_suspended.set()
185+
self.assertFalse(write_finished.wait(0.5),
186+
'output was not suspended')
187+
finally:
188+
fcntl.ioctl(wfd, termios.TCXONC, termios.TCOON)
189+
self.assertTrue(write_finished.wait(0.5),
190+
'output was not resumed')
191+
self.assertEqual(os.read(rfd, 1024), b'def')
147192

148193
def test_ioctl_signed_unsigned_code_param(self):
149194
if termios.TIOCSWINSZ < 0:

Lib/test/test_termios.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
import os
33
import sys
44
import tempfile
5+
import threading
56
import unittest
7+
from test import support
8+
from test.support import threading_helper
69
from test.support.import_helper import import_module
710

811
termios = import_module('termios')
@@ -12,8 +15,8 @@
1215
class TestFunctions(unittest.TestCase):
1316

1417
def setUp(self):
15-
master_fd, self.fd = os.openpty()
16-
self.addCleanup(os.close, master_fd)
18+
self.master_fd, self.fd = os.openpty()
19+
self.addCleanup(os.close, self.master_fd)
1720
self.stream = self.enterContext(open(self.fd, 'wb', buffering=0))
1821
tmp = self.enterContext(tempfile.TemporaryFile(mode='wb', buffering=0))
1922
self.bad_fd = tmp.fileno()
@@ -136,6 +139,29 @@ def test_tcflush_errors(self):
136139
self.assertRaises(TypeError, termios.tcflush, object(), termios.TCIFLUSH)
137140
self.assertRaises(TypeError, termios.tcflush, self.fd)
138141

142+
def test_tcflush_clear_input_or_output(self):
143+
wfd = self.fd
144+
rfd = self.master_fd
145+
inbuf = sys.platform == 'linux'
146+
147+
os.write(wfd, b'abcdef')
148+
self.assertEqual(os.read(rfd, 2), b'ab')
149+
if inbuf:
150+
# don't flush input
151+
termios.tcflush(rfd, termios.TCOFLUSH)
152+
else:
153+
# don't flush output
154+
termios.tcflush(wfd, termios.TCIFLUSH)
155+
self.assertEqual(os.read(rfd, 2), b'cd')
156+
if inbuf:
157+
# flush input
158+
termios.tcflush(rfd, termios.TCIFLUSH)
159+
else:
160+
# flush output
161+
termios.tcflush(wfd, termios.TCOFLUSH)
162+
os.write(wfd, b'ABCDEF')
163+
self.assertEqual(os.read(rfd, 1024), b'ABCDEF')
164+
139165
def test_tcflow(self):
140166
termios.tcflow(self.fd, termios.TCOOFF)
141167
termios.tcflow(self.fd, termios.TCOON)
@@ -152,6 +178,32 @@ def test_tcflow_errors(self):
152178
self.assertRaises(TypeError, termios.tcflow, object(), termios.TCOON)
153179
self.assertRaises(TypeError, termios.tcflow, self.fd)
154180

181+
@unittest.skipUnless(sys.platform == 'linux', 'only works on Linux')
182+
def test_tcflow_suspend_and_resume_output(self):
183+
wfd = self.fd
184+
rfd = self.master_fd
185+
write_suspended = threading.Event()
186+
write_finished = threading.Event()
187+
188+
def writer():
189+
os.write(wfd, b'abc')
190+
write_suspended.wait()
191+
os.write(wfd, b'def')
192+
write_finished.set()
193+
194+
with threading_helper.start_threads([threading.Thread(target=writer)]):
195+
self.assertEqual(os.read(rfd, 3), b'abc')
196+
try:
197+
termios.tcflow(wfd, termios.TCOOFF)
198+
write_suspended.set()
199+
self.assertFalse(write_finished.wait(0.5),
200+
'output was not suspended')
201+
finally:
202+
termios.tcflow(wfd, termios.TCOON)
203+
self.assertTrue(write_finished.wait(0.5),
204+
'output was not resumed')
205+
self.assertEqual(os.read(rfd, 1024), b'def')
206+
155207
def test_tcgetwinsize(self):
156208
size = termios.tcgetwinsize(self.fd)
157209
self.assertIsInstance(size, tuple)

0 commit comments

Comments
 (0)