name: "PATH search" # # An empty entry in PATH names the working directory, so a command found through one is # reported with a leading "./". brush finds the command but reports the bare name; the # `known_failure` cases below cover each surface that exposes the difference. They share # one root cause in the path search, so they should be retired together. # cases: - name: "empty PATH entry is reported as the working directory" known_failure: true # TODO(path): an empty PATH entry should resolve as "." stdin: | printf '#!/bin/sh\necho ran\n' > prog chmod +x prog PATH=":$PATH" echo "[type]" type prog; echo "rc=$?" echo "[type -p]" type -p prog; echo "rc=$?" echo "[type -P]" type -P prog; echo "rc=$?" echo "[type -a]" type -a prog; echo "rc=$?" echo "[command -v]" command -v prog; echo "rc=$?" - name: "empty PATH entry is reported the same wherever it appears" known_failure: true # TODO(path): an empty PATH entry should resolve as "." stdin: | printf '#!/bin/sh\necho ran\n' > prog chmod +x prog original=$PATH # Each of these keeps the rest of PATH reachable, so only the empty entry can # account for finding `prog`, which exists solely in the working directory. echo "[leading]" PATH=":$original"; hash -r command -v prog; echo "rc=$?" echo "[trailing]" PATH="$original:"; hash -r command -v prog; echo "rc=$?" echo "[middle]" PATH="/nonexistent::$original"; hash -r command -v prog; echo "rc=$?" - name: "empty PATH entry is hashed as the working directory" known_failure: true # TODO(path): an empty PATH entry should resolve as "." stdin: | printf '#!/bin/sh\necho ran\n' > prog chmod +x prog PATH=":$PATH" hash -r hash prog; echo "rc=$?" hash -t prog; echo "rc=$?" - name: "empty PATH entry is passed to the command as the working directory" known_failure: true # TODO(path): an empty PATH entry should resolve as "." stdin: | # A copy of env, found through the empty entry before the real one, reports the _ # it's started with: the path the shell ran it by. (It keeps env's name, which a # multi-call binary such as NixOS's coreutils goes by.) cp "$(command -v env)" env PATH=":$PATH" env | grep '^_=' - name: "relative PATH entry is relative to the working directory" known_failure: true # TODO(path): a relative PATH entry is searched relative to the directory brush started in, not the shell's working directory test_files: - path: "sub/bin/prog" executable: true contents: | #!/bin/sh echo "ran prog" stdin: | cd sub PATH=bin type prog type -a prog command -v prog hash prog; echo "hash: $?" prog; echo "command: $?" (exec prog); echo "exec in a subshell: $?" exec prog - name: "script run by a relative path sees that path as its $0" known_failure: true # TODO(path): a relative PATH entry is searched relative to the directory brush started in, not the shell's working directory test_files: - path: "sub/prog" executable: true contents: | #!/bin/sh echo "0=$0" - path: "sub/bin/prog" executable: true contents: | #!/bin/sh echo "0=$0" stdin: | cd sub ./prog PATH=bin prog - name: "empty PATH entry runs the program in the working directory" known_failure: true # TODO(path): an empty PATH entry should resolve as "." test_files: - path: "sub/id" executable: true contents: | #!/bin/sh echo "id in the working directory" - path: "sub/only-here" executable: true contents: | #!/bin/sh echo "only here" stdin: | # Unexported, so that the program can't be found again through the PATH a # process is given, only as the shell found it. cd sub export -n PATH PATH=":$PATH" id only-here type only-here - name: "command found through an empty PATH entry is searched for again after cd" known_failure: true # TODO(hash): a command hashed as found in the working directory isn't searched for again after cd test_files: - path: "a/prog" executable: true contents: | #!/bin/sh echo "prog in a" - path: "b/.keep" - path: "fallback/prog" executable: true contents: | #!/bin/sh echo "prog in fallback" stdin: | for first in "" "."; do export PATH="${first}:${PWD}/fallback:${PATH}" cd a prog hash -t prog; type prog cd ../b prog; echo "status: $?" cd ../a prog hash -t prog; type prog cd .. export PATH="${PATH#"${first}:${PWD}/fallback:"}" done - name: "PATH entry for the working directory written another way" known_failure: true # TODO(path): a PATH entry such as `./` or `../dir` isn't taken to be the working directory test_files: - path: "a/prog" executable: true contents: | #!/bin/sh echo "prog in a" - path: "b/.keep" - path: "fallback/prog" executable: true contents: | #!/bin/sh echo "prog in fallback" stdin: | fallback="${PWD}/fallback" for first in ./ ./. .// ../b; do PATH="${first}:${fallback}" hash -r cd b prog cd ../a prog hash -t prog cd .. done - name: "command found through a PATH entry is run by the path that entry gives it" test_files: - path: "bin/prog" executable: true contents: | #!/bin/sh echo "0=$0" stdin: | PATH=./bin// prog PATH=.//bin hash -r prog - name: "PATH entry starting with a dot is the working directory only if it's the same" known_failure: true # TODO(path): a PATH entry that's the working directory under another name isn't taken to be it test_files: - path: "a/prog" executable: true contents: | #!/bin/sh echo "prog in a" - path: "b/sub/.keep" - path: "b/.hidden/.keep" - path: "fallback/prog" executable: true contents: | #!/bin/sh echo "prog in fallback" stdin: | fallback="${PWD}/fallback" ln -s . b/self for first in ./sub .hidden .nosuch ./self; do echo "[${first}]" PATH="${first}:${fallback}" hash -r cd b prog cd ../a prog cd .. done - name: "command hashed with hash -p is kept with an empty PATH entry" test_files: - path: "prog" executable: true contents: | #!/bin/sh echo "prog in the working directory" stdin: | true_path=$(type -P true) PATH=":$PATH" hash -p "${true_path}" prog prog; echo "status: $?" - name: "with PATH unset, a command in the working directory is run by its name" known_failure: true # TODO(path): with PATH unset, a command in the working directory is run as ./name, not by its bare name test_files: - path: "prog" executable: true contents: | #!/bin/sh echo "0=$0" stdin: | unset PATH prog - name: "relative PATH entry is hashed as bash hashes it" known_failure: true # TODO(hash): bash hashes a command found through a relative PATH entry as ./entry/name test_files: - path: "sub/bin/prog" executable: true contents: | #!/bin/sh echo "ran prog" stdin: | cd sub PATH=bin prog hash -t prog # # These two surfaces already agree with the oracle; they guard the parts of the # behavior that work, so that a fix for the cases above doesn't regress them. # - name: "a command found through an empty PATH entry is runnable" stdin: | printf '#!/bin/sh\necho ran\n' > prog chmod +x prog PATH=":$PATH" prog; echo "rc=$?" - name: "command -V absolutizes a command found through an empty PATH entry" stdin: | # Normalize away the temporary directory the test runs in. scrub() { sed -E "s|$PWD||g"; } printf '#!/bin/sh\necho ran\n' > prog chmod +x prog PATH=":$PATH" command -V prog | scrub; echo "rc=${PIPESTATUS[0]}"