Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 73 additions & 48 deletions Lib/test/test_fcntl.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,24 @@
"""Test program for the fcntl C module.
"""
import errno
import multiprocessing
import platform
import os
import struct
import sys
import unittest
from multiprocessing import Process
from test.support import verbose, cpython_only
from test.support import (
cpython_only, get_pagesize, is_apple, requires_subprocess, verbose, is_emscripten
)
from test.support.import_helper import import_module
from test.support.os_helper import TESTFN, unlink
from test.support.os_helper import TESTFN, unlink, make_bad_fd


# Skip test if no fcntl module.
fcntl = import_module('fcntl')



def get_lockdata():
try:
os.O_LARGEFILE
except AttributeError:
start_len = "ll"
else:
start_len = "qq"

if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd'))
or sys.platform == 'darwin'):
if struct.calcsize('l') == 8:
off_t = 'l'
pid_t = 'i'
else:
off_t = 'lxxxx'
pid_t = 'l'
lockdata = struct.pack(off_t + off_t + pid_t + 'hh', 0, 0, 0,
fcntl.F_WRLCK, 0)
elif sys.platform.startswith('gnukfreebsd'):
lockdata = struct.pack('qqihhi', 0, 0, 0, fcntl.F_WRLCK, 0, 0)
elif sys.platform in ['hp-uxB', 'unixware7']:
lockdata = struct.pack('hhlllii', fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0)
else:
lockdata = struct.pack('hh'+start_len+'hh', fcntl.F_WRLCK, 0, 0, 0, 0, 0)
if lockdata:
if verbose:
print('struct.pack: ', repr(lockdata))
return lockdata

lockdata = get_lockdata()

class BadFile:
def __init__(self, fn):
self.fn = fn
Expand Down Expand Up @@ -78,12 +50,45 @@ def tearDown(self):
self.f.close()
unlink(TESTFN)

@staticmethod
def get_lockdata():
try:
os.O_LARGEFILE
except AttributeError:
start_len = "ll"
else:
start_len = "qq"

if (
sys.platform.startswith(('netbsd', 'freebsd', 'openbsd'))
or is_apple
):
if struct.calcsize('l') == 8:
off_t = 'l'
pid_t = 'i'
else:
off_t = 'lxxxx'
pid_t = 'l'
lockdata = struct.pack(off_t + off_t + pid_t + 'hh', 0, 0, 0,
fcntl.F_WRLCK, 0)
elif sys.platform.startswith('gnukfreebsd'):
lockdata = struct.pack('qqihhi', 0, 0, 0, fcntl.F_WRLCK, 0, 0)
elif sys.platform in ['hp-uxB', 'unixware7']:
lockdata = struct.pack('hhlllii', fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0)
else:
lockdata = struct.pack('hh'+start_len+'hh', fcntl.F_WRLCK, 0, 0, 0, 0, 0)
if lockdata:
if verbose:
print('struct.pack: ', repr(lockdata))
return lockdata

def test_fcntl_fileno(self):
# the example from the library docs
self.f = open(TESTFN, 'wb')
rv = fcntl.fcntl(self.f.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
if verbose:
print('Status from fcntl with O_NONBLOCK: ', rv)
lockdata = self.get_lockdata()
rv = fcntl.fcntl(self.f.fileno(), fcntl.F_SETLKW, lockdata)
if verbose:
print('String from fcntl with F_SETLKW: ', repr(rv))
Expand All @@ -95,6 +100,7 @@ def test_fcntl_file_descriptor(self):
rv = fcntl.fcntl(self.f, fcntl.F_SETFL, os.O_NONBLOCK)
if verbose:
print('Status from fcntl with O_NONBLOCK: ', rv)
lockdata = self.get_lockdata()
rv = fcntl.fcntl(self.f, fcntl.F_SETLKW, lockdata)
if verbose:
print('String from fcntl with F_SETLKW: ', repr(rv))
Expand All @@ -112,7 +118,9 @@ def test_fcntl_bad_file(self):

@cpython_only
def test_fcntl_bad_file_overflow(self):
from _testcapi import INT_MAX, INT_MIN
_testcapi = import_module("_testcapi")
INT_MAX = _testcapi.INT_MAX
INT_MIN = _testcapi.INT_MIN
# Issue 15989
with self.assertRaises(OverflowError):
fcntl.fcntl(INT_MAX + 1, fcntl.F_SETFL, os.O_NONBLOCK)
Expand All @@ -124,19 +132,25 @@ def test_fcntl_bad_file_overflow(self):
fcntl.fcntl(BadFile(INT_MIN - 1), fcntl.F_SETFL, os.O_NONBLOCK)

@unittest.skipIf(
platform.machine().startswith('arm') and platform.system() == 'Linux',
"ARM Linux returns EINVAL for F_NOTIFY DN_MULTISHOT")
(platform.machine().startswith("arm") and platform.system() == "Linux")
or platform.system() == "Android",
"this platform returns EINVAL for F_NOTIFY DN_MULTISHOT")
def test_fcntl_64_bit(self):
# Issue #1309352: fcntl shouldn't fail when the third arg fits in a
# Issue GH-42434: fcntl shouldn't fail when the third arg fits in a
# C 'long' but not in a C 'int'.
try:
cmd = fcntl.F_NOTIFY
# This flag is larger than 2**31 in 64-bit builds
# DN_MULTISHOT is >= 2**31 in 64-bit builds
flags = fcntl.DN_MULTISHOT
except AttributeError:
self.skipTest("F_NOTIFY or DN_MULTISHOT unavailable")
fd = os.open(os.path.dirname(os.path.abspath(TESTFN)), os.O_RDONLY)
try:
try:
fcntl.fcntl(fd, cmd, fcntl.DN_DELETE)
except OSError as exc:
if exc.errno == errno.EINVAL:
self.skipTest("F_NOTIFY not available by this environment")
fcntl.fcntl(fd, cmd, flags)
finally:
os.close(fd)
Expand All @@ -155,35 +169,35 @@ def test_flock(self):
self.assertRaises(ValueError, fcntl.flock, -1, fcntl.LOCK_SH)
self.assertRaises(TypeError, fcntl.flock, 'spam', fcntl.LOCK_SH)

# TODO RustPython
@unittest.skipUnless(sys.platform == 'linux', 'test requires Linux')
@unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError")
@requires_subprocess()
def test_lockf_exclusive(self):
self.f = open(TESTFN, 'wb+')
cmd = fcntl.LOCK_EX | fcntl.LOCK_NB
fcntl.lockf(self.f, cmd)
p = Process(target=try_lockf_on_other_process_fail, args=(TESTFN, cmd))
mp = multiprocessing.get_context('spawn')
p = mp.Process(target=try_lockf_on_other_process_fail, args=(TESTFN, cmd))
p.start()
p.join()
fcntl.lockf(self.f, fcntl.LOCK_UN)
self.assertEqual(p.exitcode, 0)

# TODO RustPython
@unittest.skipUnless(sys.platform == 'linux', 'test requires Linux')
@unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError")
@requires_subprocess()
def test_lockf_share(self):
self.f = open(TESTFN, 'wb+')
cmd = fcntl.LOCK_SH | fcntl.LOCK_NB
fcntl.lockf(self.f, cmd)
p = Process(target=try_lockf_on_other_process, args=(TESTFN, cmd))
mp = multiprocessing.get_context('spawn')
p = mp.Process(target=try_lockf_on_other_process, args=(TESTFN, cmd))
p.start()
p.join()
fcntl.lockf(self.f, fcntl.LOCK_UN)
self.assertEqual(p.exitcode, 0)

@cpython_only
def test_flock_overflow(self):
import _testcapi
_testcapi = import_module("_testcapi")
self.assertRaises(OverflowError, fcntl.flock, _testcapi.INT_MAX+1,
fcntl.LOCK_SH)

Expand All @@ -197,14 +211,16 @@ def test_fcntl_f_getpath(self):
@unittest.skipUnless(
hasattr(fcntl, "F_SETPIPE_SZ") and hasattr(fcntl, "F_GETPIPE_SZ"),
"F_SETPIPE_SZ and F_GETPIPE_SZ are not available on all platforms.")
@unittest.skipIf(is_emscripten, "Emscripten pipefs doesn't support these")
def test_fcntl_f_pipesize(self):
test_pipe_r, test_pipe_w = os.pipe()
try:
# Get the default pipesize with F_GETPIPE_SZ
pipesize_default = fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ)
pipesize = pipesize_default // 2 # A new value to detect change.
if pipesize < 512: # the POSIX minimum
raise unittest.SkitTest(
pagesize_default = get_pagesize()
if pipesize < pagesize_default: # the POSIX minimum
raise unittest.SkipTest(
'default pipesize too small to perform test.')
fcntl.fcntl(test_pipe_w, fcntl.F_SETPIPE_SZ, pipesize)
self.assertEqual(fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ),
Expand All @@ -213,6 +229,15 @@ def test_fcntl_f_pipesize(self):
os.close(test_pipe_r)
os.close(test_pipe_w)

@unittest.skipUnless(hasattr(fcntl, 'F_DUPFD'), 'need fcntl.F_DUPFD')
def test_bad_fd(self):
# gh-134744: Test error handling
fd = make_bad_fd()
with self.assertRaises(OSError):
fcntl.fcntl(fd, fcntl.F_DUPFD, 0)
with self.assertRaises(OSError):
fcntl.fcntl(fd, fcntl.F_DUPFD, b'\0' * 1024)


if __name__ == '__main__':
unittest.main()
Loading
Loading