11"""Test program for the fcntl C module.
22"""
3+ import errno
4+ import multiprocessing
35import platform
46import os
57import struct
68import sys
79import unittest
8- from multiprocessing import Process
9- from test .support import verbose , cpython_only
10+ from test .support import (
11+ cpython_only , get_pagesize , is_apple , requires_subprocess , verbose , is_emscripten
12+ )
1013from test .support .import_helper import import_module
11- from test .support .os_helper import TESTFN , unlink
14+ from test .support .os_helper import TESTFN , unlink , make_bad_fd
1215
1316
1417# Skip test if no fcntl module.
1518fcntl = import_module ('fcntl' )
1619
1720
1821
19- def get_lockdata ():
20- try :
21- os .O_LARGEFILE
22- except AttributeError :
23- start_len = "ll"
24- else :
25- start_len = "qq"
26-
27- if (sys .platform .startswith (('netbsd' , 'freebsd' , 'openbsd' ))
28- or sys .platform == 'darwin' ):
29- if struct .calcsize ('l' ) == 8 :
30- off_t = 'l'
31- pid_t = 'i'
32- else :
33- off_t = 'lxxxx'
34- pid_t = 'l'
35- lockdata = struct .pack (off_t + off_t + pid_t + 'hh' , 0 , 0 , 0 ,
36- fcntl .F_WRLCK , 0 )
37- elif sys .platform .startswith ('gnukfreebsd' ):
38- lockdata = struct .pack ('qqihhi' , 0 , 0 , 0 , fcntl .F_WRLCK , 0 , 0 )
39- elif sys .platform in ['hp-uxB' , 'unixware7' ]:
40- lockdata = struct .pack ('hhlllii' , fcntl .F_WRLCK , 0 , 0 , 0 , 0 , 0 , 0 )
41- else :
42- lockdata = struct .pack ('hh' + start_len + 'hh' , fcntl .F_WRLCK , 0 , 0 , 0 , 0 , 0 )
43- if lockdata :
44- if verbose :
45- print ('struct.pack: ' , repr (lockdata ))
46- return lockdata
47-
48- lockdata = get_lockdata ()
49-
5022class BadFile :
5123 def __init__ (self , fn ):
5224 self .fn = fn
@@ -78,12 +50,45 @@ def tearDown(self):
7850 self .f .close ()
7951 unlink (TESTFN )
8052
53+ @staticmethod
54+ def get_lockdata ():
55+ try :
56+ os .O_LARGEFILE
57+ except AttributeError :
58+ start_len = "ll"
59+ else :
60+ start_len = "qq"
61+
62+ if (
63+ sys .platform .startswith (('netbsd' , 'freebsd' , 'openbsd' ))
64+ or is_apple
65+ ):
66+ if struct .calcsize ('l' ) == 8 :
67+ off_t = 'l'
68+ pid_t = 'i'
69+ else :
70+ off_t = 'lxxxx'
71+ pid_t = 'l'
72+ lockdata = struct .pack (off_t + off_t + pid_t + 'hh' , 0 , 0 , 0 ,
73+ fcntl .F_WRLCK , 0 )
74+ elif sys .platform .startswith ('gnukfreebsd' ):
75+ lockdata = struct .pack ('qqihhi' , 0 , 0 , 0 , fcntl .F_WRLCK , 0 , 0 )
76+ elif sys .platform in ['hp-uxB' , 'unixware7' ]:
77+ lockdata = struct .pack ('hhlllii' , fcntl .F_WRLCK , 0 , 0 , 0 , 0 , 0 , 0 )
78+ else :
79+ lockdata = struct .pack ('hh' + start_len + 'hh' , fcntl .F_WRLCK , 0 , 0 , 0 , 0 , 0 )
80+ if lockdata :
81+ if verbose :
82+ print ('struct.pack: ' , repr (lockdata ))
83+ return lockdata
84+
8185 def test_fcntl_fileno (self ):
8286 # the example from the library docs
8387 self .f = open (TESTFN , 'wb' )
8488 rv = fcntl .fcntl (self .f .fileno (), fcntl .F_SETFL , os .O_NONBLOCK )
8589 if verbose :
8690 print ('Status from fcntl with O_NONBLOCK: ' , rv )
91+ lockdata = self .get_lockdata ()
8792 rv = fcntl .fcntl (self .f .fileno (), fcntl .F_SETLKW , lockdata )
8893 if verbose :
8994 print ('String from fcntl with F_SETLKW: ' , repr (rv ))
@@ -95,6 +100,7 @@ def test_fcntl_file_descriptor(self):
95100 rv = fcntl .fcntl (self .f , fcntl .F_SETFL , os .O_NONBLOCK )
96101 if verbose :
97102 print ('Status from fcntl with O_NONBLOCK: ' , rv )
103+ lockdata = self .get_lockdata ()
98104 rv = fcntl .fcntl (self .f , fcntl .F_SETLKW , lockdata )
99105 if verbose :
100106 print ('String from fcntl with F_SETLKW: ' , repr (rv ))
@@ -112,7 +118,9 @@ def test_fcntl_bad_file(self):
112118
113119 @cpython_only
114120 def test_fcntl_bad_file_overflow (self ):
115- from _testcapi import INT_MAX , INT_MIN
121+ _testcapi = import_module ("_testcapi" )
122+ INT_MAX = _testcapi .INT_MAX
123+ INT_MIN = _testcapi .INT_MIN
116124 # Issue 15989
117125 with self .assertRaises (OverflowError ):
118126 fcntl .fcntl (INT_MAX + 1 , fcntl .F_SETFL , os .O_NONBLOCK )
@@ -124,19 +132,25 @@ def test_fcntl_bad_file_overflow(self):
124132 fcntl .fcntl (BadFile (INT_MIN - 1 ), fcntl .F_SETFL , os .O_NONBLOCK )
125133
126134 @unittest .skipIf (
127- platform .machine ().startswith ('arm' ) and platform .system () == 'Linux' ,
128- "ARM Linux returns EINVAL for F_NOTIFY DN_MULTISHOT" )
135+ (platform .machine ().startswith ("arm" ) and platform .system () == "Linux" )
136+ or platform .system () == "Android" ,
137+ "this platform returns EINVAL for F_NOTIFY DN_MULTISHOT" )
129138 def test_fcntl_64_bit (self ):
130- # Issue #1309352 : fcntl shouldn't fail when the third arg fits in a
139+ # Issue GH-42434 : fcntl shouldn't fail when the third arg fits in a
131140 # C 'long' but not in a C 'int'.
132141 try :
133142 cmd = fcntl .F_NOTIFY
134- # This flag is larger than 2**31 in 64-bit builds
143+ # DN_MULTISHOT is >= 2**31 in 64-bit builds
135144 flags = fcntl .DN_MULTISHOT
136145 except AttributeError :
137146 self .skipTest ("F_NOTIFY or DN_MULTISHOT unavailable" )
138147 fd = os .open (os .path .dirname (os .path .abspath (TESTFN )), os .O_RDONLY )
139148 try :
149+ try :
150+ fcntl .fcntl (fd , cmd , fcntl .DN_DELETE )
151+ except OSError as exc :
152+ if exc .errno == errno .EINVAL :
153+ self .skipTest ("F_NOTIFY not available by this environment" )
140154 fcntl .fcntl (fd , cmd , flags )
141155 finally :
142156 os .close (fd )
@@ -155,35 +169,35 @@ def test_flock(self):
155169 self .assertRaises (ValueError , fcntl .flock , - 1 , fcntl .LOCK_SH )
156170 self .assertRaises (TypeError , fcntl .flock , 'spam' , fcntl .LOCK_SH )
157171
158- # TODO RustPython
159- @unittest .skipUnless (sys .platform == 'linux' , 'test requires Linux' )
160172 @unittest .skipIf (platform .system () == "AIX" , "AIX returns PermissionError" )
173+ @requires_subprocess ()
161174 def test_lockf_exclusive (self ):
162175 self .f = open (TESTFN , 'wb+' )
163176 cmd = fcntl .LOCK_EX | fcntl .LOCK_NB
164177 fcntl .lockf (self .f , cmd )
165- p = Process (target = try_lockf_on_other_process_fail , args = (TESTFN , cmd ))
178+ mp = multiprocessing .get_context ('spawn' )
179+ p = mp .Process (target = try_lockf_on_other_process_fail , args = (TESTFN , cmd ))
166180 p .start ()
167181 p .join ()
168182 fcntl .lockf (self .f , fcntl .LOCK_UN )
169183 self .assertEqual (p .exitcode , 0 )
170184
171- # TODO RustPython
172- @unittest .skipUnless (sys .platform == 'linux' , 'test requires Linux' )
173185 @unittest .skipIf (platform .system () == "AIX" , "AIX returns PermissionError" )
186+ @requires_subprocess ()
174187 def test_lockf_share (self ):
175188 self .f = open (TESTFN , 'wb+' )
176189 cmd = fcntl .LOCK_SH | fcntl .LOCK_NB
177190 fcntl .lockf (self .f , cmd )
178- p = Process (target = try_lockf_on_other_process , args = (TESTFN , cmd ))
191+ mp = multiprocessing .get_context ('spawn' )
192+ p = mp .Process (target = try_lockf_on_other_process , args = (TESTFN , cmd ))
179193 p .start ()
180194 p .join ()
181195 fcntl .lockf (self .f , fcntl .LOCK_UN )
182196 self .assertEqual (p .exitcode , 0 )
183197
184198 @cpython_only
185199 def test_flock_overflow (self ):
186- import _testcapi
200+ _testcapi = import_module ( " _testcapi" )
187201 self .assertRaises (OverflowError , fcntl .flock , _testcapi .INT_MAX + 1 ,
188202 fcntl .LOCK_SH )
189203
@@ -197,14 +211,16 @@ def test_fcntl_f_getpath(self):
197211 @unittest .skipUnless (
198212 hasattr (fcntl , "F_SETPIPE_SZ" ) and hasattr (fcntl , "F_GETPIPE_SZ" ),
199213 "F_SETPIPE_SZ and F_GETPIPE_SZ are not available on all platforms." )
214+ @unittest .skipIf (is_emscripten , "Emscripten pipefs doesn't support these" )
200215 def test_fcntl_f_pipesize (self ):
201216 test_pipe_r , test_pipe_w = os .pipe ()
202217 try :
203218 # Get the default pipesize with F_GETPIPE_SZ
204219 pipesize_default = fcntl .fcntl (test_pipe_w , fcntl .F_GETPIPE_SZ )
205220 pipesize = pipesize_default // 2 # A new value to detect change.
206- if pipesize < 512 : # the POSIX minimum
207- raise unittest .SkitTest (
221+ pagesize_default = get_pagesize ()
222+ if pipesize < pagesize_default : # the POSIX minimum
223+ raise unittest .SkipTest (
208224 'default pipesize too small to perform test.' )
209225 fcntl .fcntl (test_pipe_w , fcntl .F_SETPIPE_SZ , pipesize )
210226 self .assertEqual (fcntl .fcntl (test_pipe_w , fcntl .F_GETPIPE_SZ ),
@@ -213,6 +229,15 @@ def test_fcntl_f_pipesize(self):
213229 os .close (test_pipe_r )
214230 os .close (test_pipe_w )
215231
232+ @unittest .skipUnless (hasattr (fcntl , 'F_DUPFD' ), 'need fcntl.F_DUPFD' )
233+ def test_bad_fd (self ):
234+ # gh-134744: Test error handling
235+ fd = make_bad_fd ()
236+ with self .assertRaises (OSError ):
237+ fcntl .fcntl (fd , fcntl .F_DUPFD , 0 )
238+ with self .assertRaises (OSError ):
239+ fcntl .fcntl (fd , fcntl .F_DUPFD , b'\0 ' * 1024 )
240+
216241
217242if __name__ == '__main__' :
218243 unittest .main ()
0 commit comments