OLD | NEW |
(Empty) | |
| 1 # -*- coding: utf-8 -*- |
| 2 import subprocess |
| 3 import tempfile |
| 4 import shutil |
| 5 import errno |
| 6 import os |
| 7 |
| 8 import pexpect |
| 9 from . import PexpectTestCase |
| 10 |
| 11 import pytest |
| 12 |
| 13 |
| 14 class TestCaseWhich(PexpectTestCase.PexpectTestCase): |
| 15 " Tests for pexpect.which(). " |
| 16 |
| 17 def test_which_finds_ls(self): |
| 18 " which() can find ls(1). " |
| 19 exercise = pexpect.which("ls") |
| 20 assert exercise is not None |
| 21 assert exercise.startswith('/') |
| 22 |
| 23 def test_os_defpath_which(self): |
| 24 " which() finds an executable in $PATH and returns its abspath. " |
| 25 |
| 26 bin_dir = tempfile.mkdtemp() |
| 27 temp_obj = tempfile.NamedTemporaryFile( |
| 28 suffix=u'.sh', prefix=u'ǝpoɔıun-', |
| 29 dir=bin_dir, delete=False) |
| 30 bin_path = temp_obj.name |
| 31 fname = os.path.basename(temp_obj.name) |
| 32 save_path = os.environ['PATH'] |
| 33 save_defpath = os.defpath |
| 34 |
| 35 try: |
| 36 # setup |
| 37 os.environ['PATH'] = '' |
| 38 os.defpath = bin_dir |
| 39 with open(bin_path, 'w') as fp: |
| 40 pass |
| 41 |
| 42 # given non-executable, |
| 43 os.chmod(bin_path, 0o400) |
| 44 |
| 45 # exercise absolute and relative, |
| 46 assert pexpect.which(bin_path) is None |
| 47 assert pexpect.which(fname) is None |
| 48 |
| 49 # given executable, |
| 50 os.chmod(bin_path, 0o700) |
| 51 |
| 52 # exercise absolute and relative, |
| 53 assert pexpect.which(bin_path) == bin_path |
| 54 assert pexpect.which(fname) == bin_path |
| 55 |
| 56 finally: |
| 57 # restore, |
| 58 os.environ['PATH'] = save_path |
| 59 os.defpath = save_defpath |
| 60 |
| 61 # destroy scratch files and folders, |
| 62 if os.path.exists(bin_path): |
| 63 os.unlink(bin_path) |
| 64 if os.path.exists(bin_dir): |
| 65 os.rmdir(bin_dir) |
| 66 |
| 67 def test_path_search_which(self): |
| 68 " which() finds an executable in $PATH and returns its abspath. " |
| 69 fname = 'gcc' |
| 70 bin_dir = tempfile.mkdtemp() |
| 71 bin_path = os.path.join(bin_dir, fname) |
| 72 save_path = os.environ['PATH'] |
| 73 try: |
| 74 # setup |
| 75 os.environ['PATH'] = bin_dir |
| 76 with open(bin_path, 'w') as fp: |
| 77 pass |
| 78 |
| 79 # given non-executable, |
| 80 os.chmod(bin_path, 0o400) |
| 81 |
| 82 # exercise absolute and relative, |
| 83 assert pexpect.which(bin_path) is None |
| 84 assert pexpect.which(fname) is None |
| 85 |
| 86 # given executable, |
| 87 os.chmod(bin_path, 0o700) |
| 88 |
| 89 # exercise absolute and relative, |
| 90 assert pexpect.which(bin_path) == bin_path |
| 91 assert pexpect.which(fname) == bin_path |
| 92 |
| 93 finally: |
| 94 # restore, |
| 95 os.environ['PATH'] = save_path |
| 96 |
| 97 # destroy scratch files and folders, |
| 98 if os.path.exists(bin_path): |
| 99 os.unlink(bin_path) |
| 100 if os.path.exists(bin_dir): |
| 101 os.rmdir(bin_dir) |
| 102 |
| 103 def test_which_follows_symlink(self): |
| 104 " which() follows symlinks and returns its path. " |
| 105 fname = 'original' |
| 106 symname = 'extra-crispy' |
| 107 bin_dir = tempfile.mkdtemp() |
| 108 bin_path = os.path.join(bin_dir, fname) |
| 109 sym_path = os.path.join(bin_dir, symname) |
| 110 save_path = os.environ['PATH'] |
| 111 try: |
| 112 # setup |
| 113 os.environ['PATH'] = bin_dir |
| 114 with open(bin_path, 'w') as fp: |
| 115 pass |
| 116 os.chmod(bin_path, 0o400) |
| 117 os.symlink(bin_path, sym_path) |
| 118 |
| 119 # should not be found because symlink points to non-executable |
| 120 assert pexpect.which(symname) is None |
| 121 |
| 122 # but now it should -- because it is executable |
| 123 os.chmod(bin_path, 0o700) |
| 124 assert pexpect.which(symname) == sym_path |
| 125 |
| 126 finally: |
| 127 # restore, |
| 128 os.environ['PATH'] = save_path |
| 129 |
| 130 # destroy scratch files, symlinks, and folders, |
| 131 if os.path.exists(sym_path): |
| 132 os.unlink(sym_path) |
| 133 if os.path.exists(bin_path): |
| 134 os.unlink(bin_path) |
| 135 if os.path.exists(bin_dir): |
| 136 os.rmdir(bin_dir) |
| 137 |
| 138 def test_which_should_not_match_folders(self): |
| 139 " Which does not match folders, even though they are executable. " |
| 140 # make up a path and insert a folder that is 'executable', a naive |
| 141 # implementation might match (previously pexpect versions 3.2 and |
| 142 # sh versions 1.0.8, reported by @lcm337.) |
| 143 fname = 'g++' |
| 144 bin_dir = tempfile.mkdtemp() |
| 145 bin_dir2 = os.path.join(bin_dir, fname) |
| 146 save_path = os.environ['PATH'] |
| 147 try: |
| 148 os.environ['PATH'] = bin_dir |
| 149 os.mkdir(bin_dir2, 0o755) |
| 150 # should not be found because it is not executable *file*, |
| 151 # but rather, has the executable bit set, as a good folder |
| 152 # should -- it should not be returned because it fails isdir() |
| 153 exercise = pexpect.which(fname) |
| 154 assert exercise is None |
| 155 |
| 156 finally: |
| 157 # restore, |
| 158 os.environ['PATH'] = save_path |
| 159 # destroy scratch folders, |
| 160 for _dir in (bin_dir2, bin_dir,): |
| 161 if os.path.exists(_dir): |
| 162 os.rmdir(_dir) |
| 163 |
| 164 def test_which_should_match_other_group_user(self): |
| 165 " which() returns executables by other, group, and user ownership. " |
| 166 # create an executable and test that it is found using which() for |
| 167 # each of the 'other', 'group', and 'user' permission bits. |
| 168 fname = 'g77' |
| 169 bin_dir = tempfile.mkdtemp() |
| 170 bin_path = os.path.join(bin_dir, fname) |
| 171 save_path = os.environ['PATH'] |
| 172 try: |
| 173 # setup |
| 174 os.environ['PATH'] = bin_dir |
| 175 |
| 176 # an interpreted script requires the ability to read, |
| 177 # whereas a binary program requires only to be executable. |
| 178 # |
| 179 # to gain access to a binary program, we make a copy of |
| 180 # the existing system program echo(1). |
| 181 bin_echo = None |
| 182 for pth in ('/bin/echo', '/usr/bin/echo'): |
| 183 if os.path.exists(pth): |
| 184 bin_echo = pth |
| 185 break |
| 186 bin_which = None |
| 187 for pth in ('/bin/which', '/usr/bin/which'): |
| 188 if os.path.exists(pth): |
| 189 bin_which = pth |
| 190 break |
| 191 if not bin_echo or not bin_which: |
| 192 pytest.skip('needs `echo` and `which` binaries') |
| 193 shutil.copy(bin_echo, bin_path) |
| 194 isroot = os.getuid() == 0 |
| 195 for should_match, mode in ( |
| 196 # note that although the file may have matching 'group' or |
| 197 # 'other' executable permissions, it is *not* executable |
| 198 # because the current uid is the owner of the file -- which |
| 199 # takes precedence |
| 200 (False, 0o000), # ----------, no |
| 201 (isroot, 0o001), # ---------x, no |
| 202 (isroot, 0o010), # ------x---, no |
| 203 (True, 0o100), # ---x------, yes |
| 204 (False, 0o002), # --------w-, no |
| 205 (False, 0o020), # -----w----, no |
| 206 (False, 0o200), # --w-------, no |
| 207 (isroot, 0o003), # --------wx, no |
| 208 (isroot, 0o030), # -----wx---, no |
| 209 (True, 0o300), # --wx------, yes |
| 210 (False, 0o004), # -------r--, no |
| 211 (False, 0o040), # ----r-----, no |
| 212 (False, 0o400), # -r--------, no |
| 213 (isroot, 0o005), # -------r-x, no |
| 214 (isroot, 0o050), # ----r-x---, no |
| 215 (True, 0o500), # -r-x------, yes |
| 216 (False, 0o006), # -------rw-, no |
| 217 (False, 0o060), # ----rw----, no |
| 218 (False, 0o600), # -rw-------, no |
| 219 (isroot, 0o007), # -------rwx, no |
| 220 (isroot, 0o070), # ----rwx---, no |
| 221 (True, 0o700), # -rwx------, yes |
| 222 (isroot, 0o4001), # ---S-----x, no |
| 223 (isroot, 0o4010), # ---S--x---, no |
| 224 (True, 0o4100), # ---s------, yes |
| 225 (isroot, 0o4003), # ---S----wx, no |
| 226 (isroot, 0o4030), # ---S-wx---, no |
| 227 (True, 0o4300), # --ws------, yes |
| 228 (isroot, 0o2001), # ------S--x, no |
| 229 (isroot, 0o2010), # ------s---, no |
| 230 (True, 0o2100), # ---x--S---, yes |
| 231 |
| 232 ): |
| 233 mode_str = '{0:0>4o}'.format(mode) |
| 234 |
| 235 # given file mode, |
| 236 os.chmod(bin_path, mode) |
| 237 |
| 238 # exercise whether we may execute |
| 239 can_execute = True |
| 240 try: |
| 241 subprocess.Popen(fname).wait() == 0 |
| 242 except OSError as err: |
| 243 if err.errno != errno.EACCES: |
| 244 raise |
| 245 # permission denied |
| 246 can_execute = False |
| 247 |
| 248 assert should_match == can_execute, ( |
| 249 should_match, can_execute, mode_str) |
| 250 |
| 251 # exercise whether which(1) would match |
| 252 proc = subprocess.Popen((bin_which, fname), |
| 253 env={'PATH': bin_dir}, |
| 254 stdout=subprocess.PIPE) |
| 255 bin_which_match = bool(not proc.wait()) |
| 256 assert should_match == bin_which_match, ( |
| 257 should_match, bin_which_match, mode_str) |
| 258 |
| 259 # finally, exercise pexpect's which(1) matches |
| 260 # the same. |
| 261 pexpect_match = bool(pexpect.which(fname)) |
| 262 |
| 263 assert should_match == pexpect_match == bin_which_match, ( |
| 264 should_match, pexpect_match, bin_which_match, mode_str) |
| 265 |
| 266 finally: |
| 267 # restore, |
| 268 os.environ['PATH'] = save_path |
| 269 |
| 270 # destroy scratch files and folders, |
| 271 if os.path.exists(bin_path): |
| 272 os.unlink(bin_path) |
| 273 if os.path.exists(bin_dir): |
| 274 os.rmdir(bin_dir) |
OLD | NEW |