blob: 15a8944153d745173d427543d3a014a47b1e206d [file] [log] [blame]
# -*- coding: utf-8 -*-
import subprocess
import tempfile
import shutil
import errno
import os
import pexpect
from . import PexpectTestCase
import pytest
class TestCaseWhich(PexpectTestCase.PexpectTestCase):
" Tests for pexpect.which(). "
def test_which_finds_ls(self):
" which() can find ls(1). "
exercise = pexpect.which("ls")
assert exercise is not None
assert exercise.startswith('/')
def test_path_from_env(self):
" executable found from optional env argument "
bin_name = 'pexpect-test-path-from-env'
tempdir = tempfile.mkdtemp()
try:
bin_path = os.path.join(tempdir, bin_name)
with open(bin_path, 'w') as f:
f.write('# test file not to be run')
try:
os.chmod(bin_path, 0o700)
found_path = pexpect.which(bin_name, env={'PATH': tempdir})
finally:
os.remove(bin_path)
self.assertEqual(bin_path, found_path)
finally:
os.rmdir(tempdir)
def test_os_defpath_which(self):
" which() finds an executable in $PATH and returns its abspath. "
bin_dir = tempfile.mkdtemp()
temp_obj = tempfile.NamedTemporaryFile(
suffix=u'.sh', prefix=u'ǝpoɔıun-',
dir=bin_dir, delete=False)
bin_path = temp_obj.name
fname = os.path.basename(temp_obj.name)
save_path = os.environ['PATH']
save_defpath = os.defpath
try:
# setup
os.environ['PATH'] = ''
os.defpath = bin_dir
with open(bin_path, 'w') as fp:
pass
# given non-executable,
os.chmod(bin_path, 0o400)
# exercise absolute and relative,
assert pexpect.which(bin_path) is None
assert pexpect.which(fname) is None
# given executable,
os.chmod(bin_path, 0o700)
# exercise absolute and relative,
assert pexpect.which(bin_path) == bin_path
assert pexpect.which(fname) == bin_path
finally:
# restore,
os.environ['PATH'] = save_path
os.defpath = save_defpath
# destroy scratch files and folders,
if os.path.exists(bin_path):
os.unlink(bin_path)
if os.path.exists(bin_dir):
os.rmdir(bin_dir)
def test_path_search_which(self):
" which() finds an executable in $PATH and returns its abspath. "
fname = 'gcc'
bin_dir = tempfile.mkdtemp()
bin_path = os.path.join(bin_dir, fname)
save_path = os.environ['PATH']
try:
# setup
os.environ['PATH'] = bin_dir
with open(bin_path, 'w') as fp:
pass
# given non-executable,
os.chmod(bin_path, 0o400)
# exercise absolute and relative,
assert pexpect.which(bin_path) is None
assert pexpect.which(fname) is None
# given executable,
os.chmod(bin_path, 0o700)
# exercise absolute and relative,
assert pexpect.which(bin_path) == bin_path
assert pexpect.which(fname) == bin_path
finally:
# restore,
os.environ['PATH'] = save_path
# destroy scratch files and folders,
if os.path.exists(bin_path):
os.unlink(bin_path)
if os.path.exists(bin_dir):
os.rmdir(bin_dir)
def test_which_follows_symlink(self):
" which() follows symlinks and returns its path. "
fname = 'original'
symname = 'extra-crispy'
bin_dir = tempfile.mkdtemp()
bin_path = os.path.join(bin_dir, fname)
sym_path = os.path.join(bin_dir, symname)
save_path = os.environ['PATH']
try:
# setup
os.environ['PATH'] = bin_dir
with open(bin_path, 'w') as fp:
pass
os.chmod(bin_path, 0o400)
os.symlink(bin_path, sym_path)
# should not be found because symlink points to non-executable
assert pexpect.which(symname) is None
# but now it should -- because it is executable
os.chmod(bin_path, 0o700)
assert pexpect.which(symname) == sym_path
finally:
# restore,
os.environ['PATH'] = save_path
# destroy scratch files, symlinks, and folders,
if os.path.exists(sym_path):
os.unlink(sym_path)
if os.path.exists(bin_path):
os.unlink(bin_path)
if os.path.exists(bin_dir):
os.rmdir(bin_dir)
def test_which_should_not_match_folders(self):
" Which does not match folders, even though they are executable. "
# make up a path and insert a folder that is 'executable', a naive
# implementation might match (previously pexpect versions 3.2 and
# sh versions 1.0.8, reported by @lcm337.)
fname = 'g++'
bin_dir = tempfile.mkdtemp()
bin_dir2 = os.path.join(bin_dir, fname)
save_path = os.environ['PATH']
try:
os.environ['PATH'] = bin_dir
os.mkdir(bin_dir2, 0o755)
# should not be found because it is not executable *file*,
# but rather, has the executable bit set, as a good folder
# should -- it should not be returned because it fails isdir()
exercise = pexpect.which(fname)
assert exercise is None
finally:
# restore,
os.environ['PATH'] = save_path
# destroy scratch folders,
for _dir in (bin_dir2, bin_dir,):
if os.path.exists(_dir):
os.rmdir(_dir)
def test_which_should_match_other_group_user(self):
" which() returns executables by other, group, and user ownership. "
# create an executable and test that it is found using which() for
# each of the 'other', 'group', and 'user' permission bits.
fname = 'g77'
bin_dir = tempfile.mkdtemp()
bin_path = os.path.join(bin_dir, fname)
save_path = os.environ['PATH']
try:
# setup
os.environ['PATH'] = bin_dir
# an interpreted script requires the ability to read,
# whereas a binary program requires only to be executable.
#
# to gain access to a binary program, we make a copy of
# the existing system program echo(1).
bin_echo = None
for pth in ('/bin/echo', '/usr/bin/echo'):
if os.path.exists(pth):
bin_echo = pth
break
bin_which = None
for pth in ('/bin/which', '/usr/bin/which'):
if os.path.exists(pth):
bin_which = pth
break
if not bin_echo or not bin_which:
pytest.skip('needs `echo` and `which` binaries')
shutil.copy(bin_echo, bin_path)
isroot = os.getuid() == 0
for should_match, mode in (
# note that although the file may have matching 'group' or
# 'other' executable permissions, it is *not* executable
# because the current uid is the owner of the file -- which
# takes precedence
(False, 0o000), # ----------, no
(isroot, 0o001), # ---------x, no
(isroot, 0o010), # ------x---, no
(True, 0o100), # ---x------, yes
(False, 0o002), # --------w-, no
(False, 0o020), # -----w----, no
(False, 0o200), # --w-------, no
(isroot, 0o003), # --------wx, no
(isroot, 0o030), # -----wx---, no
(True, 0o300), # --wx------, yes
(False, 0o004), # -------r--, no
(False, 0o040), # ----r-----, no
(False, 0o400), # -r--------, no
(isroot, 0o005), # -------r-x, no
(isroot, 0o050), # ----r-x---, no
(True, 0o500), # -r-x------, yes
(False, 0o006), # -------rw-, no
(False, 0o060), # ----rw----, no
(False, 0o600), # -rw-------, no
(isroot, 0o007), # -------rwx, no
(isroot, 0o070), # ----rwx---, no
(True, 0o700), # -rwx------, yes
(isroot, 0o4001), # ---S-----x, no
(isroot, 0o4010), # ---S--x---, no
(True, 0o4100), # ---s------, yes
(isroot, 0o4003), # ---S----wx, no
(isroot, 0o4030), # ---S-wx---, no
(True, 0o4300), # --ws------, yes
(isroot, 0o2001), # ------S--x, no
(isroot, 0o2010), # ------s---, no
(True, 0o2100), # ---x--S---, yes
):
mode_str = '{0:0>4o}'.format(mode)
# given file mode,
os.chmod(bin_path, mode)
# exercise whether we may execute
can_execute = True
try:
subprocess.Popen(fname).wait() == 0
except OSError as err:
if err.errno != errno.EACCES:
raise
# permission denied
can_execute = False
assert should_match == can_execute, (
should_match, can_execute, mode_str)
# exercise whether which(1) would match
proc = subprocess.Popen((bin_which, fname),
env={'PATH': bin_dir},
stdout=subprocess.PIPE)
bin_which_match = bool(not proc.wait())
assert should_match == bin_which_match, (
should_match, bin_which_match, mode_str)
# finally, exercise pexpect's which(1) matches
# the same.
pexpect_match = bool(pexpect.which(fname))
assert should_match == pexpect_match == bin_which_match, (
should_match, pexpect_match, bin_which_match, mode_str)
finally:
# restore,
os.environ['PATH'] = save_path
# destroy scratch files and folders,
if os.path.exists(bin_path):
os.unlink(bin_path)
if os.path.exists(bin_dir):
os.rmdir(bin_dir)