1414else :
1515 with tty :
1616 # Skip if another process is in foreground
17- r = fcntl .ioctl (tty , termios .TIOCGPGRP , " " )
17+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , struct . pack ( "i" , 0 ) )
1818 rpgrp = struct .unpack ("i" , r )[0 ]
1919 if rpgrp not in (os .getpgrp (), os .getsid (0 )):
2020 raise unittest .SkipTest ("Neither the process group nor the session "
2727 pty = None
2828
2929class IoctlTests (unittest .TestCase ):
30- def test_ioctl (self ):
30+ def test_ioctl_immutable_buf (self ):
3131 # If this process has been put into the background, TIOCGPGRP returns
3232 # the session ID instead of the process group id.
3333 ids = (os .getpgrp (), os .getsid (0 ))
3434 with open ("/dev/tty" , "rb" ) as tty :
35- r = fcntl .ioctl (tty , termios .TIOCGPGRP , " " )
36- rpgrp = struct .unpack ("i" , r )[0 ]
35+ # string
36+ buf = " " * 8
37+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , buf )
38+ self .assertIsInstance (r , bytes )
39+ rpgrp = memoryview (r ).cast ('i' )[0 ]
3740 self .assertIn (rpgrp , ids )
3841
39- def _check_ioctl_mutate_len (self , nbytes = None ):
42+ # bytes
43+ buf = b" " * 8
44+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , buf )
45+ self .assertIsInstance (r , bytes )
46+ rpgrp = memoryview (r ).cast ('i' )[0 ]
47+ self .assertIn (rpgrp , ids )
48+
49+ # read-only buffer
50+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , memoryview (buf ))
51+ self .assertIsInstance (r , bytes )
52+ rpgrp = memoryview (r ).cast ('i' )[0 ]
53+ self .assertIn (rpgrp , ids )
54+
55+ def test_ioctl_mutable_buf (self ):
56+ ids = (os .getpgrp (), os .getsid (0 ))
57+ with open ("/dev/tty" , "rb" ) as tty :
58+ buf = bytearray (b" " * 8 )
59+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , buf )
60+ self .assertEqual (r , 0 )
61+ rpgrp = memoryview (buf ).cast ('i' )[0 ]
62+ self .assertIn (rpgrp , ids )
63+
64+ def test_ioctl_no_mutate_buf (self ):
65+ ids = (os .getpgrp (), os .getsid (0 ))
66+ with open ("/dev/tty" , "rb" ) as tty :
67+ buf = bytearray (b" " * 8 )
68+ save_buf = bytes (buf )
69+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , buf , False )
70+ self .assertEqual (bytes (buf ), save_buf )
71+ self .assertIsInstance (r , bytes )
72+ rpgrp = memoryview (r ).cast ('i' )[0 ]
73+ self .assertIn (rpgrp , ids )
74+
75+ def _create_int_buf (self , nbytes = None ):
4076 buf = array .array ('i' )
4177 intsize = buf .itemsize
42- ids = (os .getpgrp (), os .getsid (0 ))
4378 # A fill value unlikely to be in `ids`
4479 fill = - 12345
4580 if nbytes is not None :
@@ -48,23 +83,59 @@ def _check_ioctl_mutate_len(self, nbytes=None):
4883 self .assertEqual (len (buf ) * intsize , nbytes ) # sanity check
4984 else :
5085 buf .append (fill )
86+ return buf
87+
88+ def _check_ioctl_mutate_len (self , nbytes = None ):
89+ ids = (os .getpgrp (), os .getsid (0 ))
90+ buf = self ._create_int_buf (nbytes )
5191 with open ("/dev/tty" , "rb" ) as tty :
52- r = fcntl .ioctl (tty , termios .TIOCGPGRP , buf , True )
92+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , buf )
5393 rpgrp = buf [0 ]
5494 self .assertEqual (r , 0 )
5595 self .assertIn (rpgrp , ids )
5696
97+ def _check_ioctl_not_mutate_len (self , nbytes = None ):
98+ ids = (os .getpgrp (), os .getsid (0 ))
99+ buf = self ._create_int_buf (nbytes )
100+ save_buf = bytes (buf )
101+ with open ("/dev/tty" , "rb" ) as tty :
102+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , buf , False )
103+ self .assertIsInstance (r , bytes )
104+ self .assertEqual (len (r ), len (save_buf ))
105+ self .assertEqual (bytes (buf ), save_buf )
106+ rpgrp = array .array ('i' , r )[0 ]
107+ rpgrp = memoryview (r ).cast ('i' )[0 ]
108+ self .assertIn (rpgrp , ids )
109+
110+ buf = bytes (buf )
111+ with open ("/dev/tty" , "rb" ) as tty :
112+ r = fcntl .ioctl (tty , termios .TIOCGPGRP , buf , True )
113+ self .assertIsInstance (r , bytes )
114+ self .assertEqual (len (r ), len (save_buf ))
115+ self .assertEqual (buf , save_buf )
116+ rpgrp = array .array ('i' , r )[0 ]
117+ rpgrp = memoryview (r ).cast ('i' )[0 ]
118+ self .assertIn (rpgrp , ids )
119+
57120 def test_ioctl_mutate (self ):
58121 self ._check_ioctl_mutate_len ()
122+ self ._check_ioctl_not_mutate_len ()
59123
60124 def test_ioctl_mutate_1024 (self ):
61125 # Issue #9758: a mutable buffer of exactly 1024 bytes wouldn't be
62126 # copied back after the system call.
63127 self ._check_ioctl_mutate_len (1024 )
128+ self ._check_ioctl_not_mutate_len (1024 )
64129
65130 def test_ioctl_mutate_2048 (self ):
66131 # Test with a larger buffer, just for the record.
67132 self ._check_ioctl_mutate_len (2048 )
133+ self .assertRaises (ValueError , self ._check_ioctl_not_mutate_len , 2048 )
134+
135+ def test_ioctl_tcflush (self ):
136+ with open ("/dev/tty" , "rb" ) as tty :
137+ r = fcntl .ioctl (tty , termios .TCFLSH , termios .TCIFLUSH )
138+ self .assertEqual (r , 0 )
68139
69140 @unittest .skipIf (pty is None , 'pty module required' )
70141 def test_ioctl_set_window_size (self ):
0 commit comments