042320208e
The old harness put .tests/fakebin at the front of $PATH, which fails OPEN: a package manager with no fake fell through to the real one, and an audit of this suite invoked the host's actual `brew`. The twelve one-line fakes are replaced by a single dispatcher every fake symlinks to, plus seal.sh, which builds the WHOLE of $PATH for a run — real binaries for the pure tools, fakes for anything that installs, downloads or needs root, and command-not-found for everything else. Runs go under `env -i` so they inherit nothing. Two audits ship with it: any `have`/`find_tool` probe the seal has never heard of is an error, and the four absolute paths find_tool probes are written to .shadowed so a test needing a tool to be genuinely absent can say so. The dispatcher also fails on demand (FAKE_FAIL), which is what makes the installers' failure paths reachable at all — the apt and npm one-at-a-time retries, note_fail, and cmd_install's non-zero exit had all been unreachable, and all three survived being deleted outright. _curl stands in for every remote the installer talks to and for nothing else: an unrecognised URL is a failed download, so a typo'd host shows up as the failure it would really be. The fzf fake records its argv, which is what makes the picker's bindings testable.
131 lines
5.3 KiB
Bash
Executable File
131 lines
5.3 KiB
Bash
Executable File
#!/bin/sh
|
|
# One dispatcher; every fake in this directory is a symlink to it.
|
|
#
|
|
# The old version of this file did two things wrong, and both let real bugs
|
|
# through:
|
|
#
|
|
# it always succeeded so no test could ever reach an installer's failure
|
|
# path -- the apt and npm one-at-a-time retries, the
|
|
# "did not install" report, and cmd_install's non-zero
|
|
# exit were all unreachable, and all three survived
|
|
# being deleted outright.
|
|
# it failed OPEN the fakes were PREPENDED to $PATH, so a package
|
|
# manager with no fake here fell through to the real
|
|
# one. An audit of this suite invoked the host's real
|
|
# `brew`. A fake that is missing must be an ERROR, not
|
|
# a silent hand-off to the machine.
|
|
#
|
|
# The second is fixed by seal.sh, which builds a directory holding exactly the
|
|
# tools a run may touch -- fakes for anything that installs, downloads or needs
|
|
# root, real binaries for the pure ones -- and runs dotup under `env -i` with
|
|
# that directory as the WHOLE of PATH. Anything not listed is command-not-found.
|
|
#
|
|
# ---------------------------------------------------------------- failure ---
|
|
# FAKE_FAIL is a comma-separated list of injections:
|
|
#
|
|
# FAKE_FAIL=brew brew exits 1 every time
|
|
# FAKE_FAIL=apt-get:batch apt-get fails only when asked for >1 package,
|
|
# which is what a real "unable to locate package"
|
|
# inside a batch looks like: the batch dies and
|
|
# the one-at-a-time retry is the only thing that
|
|
# saves the other thirty.
|
|
# FAKE_FAIL=npm:batch the same shape for npm.
|
|
# FAKE_FAIL=curl:deb curl fails only for the .deb download.
|
|
#
|
|
# Several may be combined: FAKE_FAIL=brew,apt-get:batch
|
|
set -u
|
|
me=${0##*/}
|
|
printf '%s %s\n' "$me" "$*" >> "${DOTUP_TEST_LOG:?DOTUP_TEST_LOG unset}"
|
|
|
|
# The mode this run injects for THIS tool: "always", a tool-specific word, or
|
|
# empty for "behave".
|
|
mode=
|
|
_oldifs=$IFS
|
|
IFS=,
|
|
for _e in ${FAKE_FAIL:-}; do
|
|
case $_e in
|
|
"$me") mode=always; break ;;
|
|
"$me":*) mode=${_e#*:}; break ;;
|
|
esac
|
|
done
|
|
IFS=$_oldifs
|
|
[ "$mode" = always ] && exit 1
|
|
|
|
# Count the package-ish arguments of a batch call: everything after the
|
|
# subcommand that is not a flag and not the flag's value.
|
|
count_pkgs() {
|
|
n=0
|
|
for a in "$@"; do
|
|
case $a in
|
|
-*|install|update|show|get|tool|-g|-y|--*) continue ;;
|
|
*=*) continue ;;
|
|
esac
|
|
n=$((n + 1))
|
|
done
|
|
printf '%s\n' "$n"
|
|
}
|
|
|
|
case $me in
|
|
apt-get)
|
|
case ${1:-} in update) exit 0 ;; esac
|
|
[ "$mode" = batch ] && [ "$(count_pkgs "$@")" -gt 1 ] && exit 1
|
|
exit 0 ;;
|
|
npm)
|
|
[ "$mode" = batch ] && [ "$(count_pkgs "$@")" -gt 1 ] && exit 1
|
|
exit 0 ;;
|
|
apt-cache)
|
|
# Stand in for a real apt cache. Two names are special, and both are real
|
|
# behaviours of stock Ubuntu that the installer has to tell apart:
|
|
#
|
|
# gh genuinely absent -- `apt-cache policy` prints nothing. The
|
|
# manifest's real example of a name that falls through to brew.
|
|
# docker-ce present in the cache but with `Candidate: (none)`, because
|
|
# something else Conflicts/Replaces it. `apt-cache show` exits
|
|
# 0 for this, which is why the old probe kept it in the batch
|
|
# and let apt refuse all thirty packages at once.
|
|
case ${1:-} in
|
|
policy)
|
|
case ${2:-} in
|
|
gh) exit 0 ;;
|
|
docker-ce) printf '%s:\n Installed: (none)\n Candidate: (none)\n' "$2"; exit 0 ;;
|
|
esac
|
|
printf '%s:\n Installed: (none)\n Candidate: 1.0-fake\n' "$2"; exit 0 ;;
|
|
esac
|
|
case ${2:-} in gh) exit 100 ;; esac
|
|
printf 'Package: %s\n' "${2:-}"; exit 0 ;;
|
|
unzip)
|
|
# `unzip -oq <zip> -d <dir>` -- produce the one member ensure_bws looks for.
|
|
d=; prev=
|
|
for a in "$@"; do [ "$prev" = -d ] && { d=$a; break; }; prev=$a; done
|
|
[ -n "$d" ] || exit 1
|
|
mkdir -p "$d" && printf '#!/bin/sh\necho "bws 2.1.0"\n' > "$d/bws" \
|
|
&& chmod 755 "$d/bws" && exit 0
|
|
exit 1 ;;
|
|
dpkg)
|
|
# install_deb fills %a in a gh: asset name from this.
|
|
case ${1:-} in --print-architecture) echo amd64 ;; esac
|
|
exit 0 ;;
|
|
chezmoi)
|
|
# `chezmoi init --apply --source DIR -c CFG URL`. Produce the shape the
|
|
# caller checks for -- a source tree with a .git in it -- and NOTHING else,
|
|
# at the caller's umask, so a missing `chmod -R go-rwx` is visible as a mode.
|
|
src=; prev=
|
|
for a in "$@"; do [ "$prev" = --source ] && { src=$a; break; }; prev=$a; done
|
|
[ -n "$src" ] || exit 0
|
|
mkdir -p "$src/.git" && : > "$src/.git/config" && : > "$src/README" || exit 1
|
|
exit 0 ;;
|
|
curl) . "$(dirname "$0")/_curl" ;;
|
|
fzf)
|
|
# The picker's own fzf, and the only fake here whose ARGV is the thing
|
|
# under test. `--bind` strings are built across a dozen continued lines, so
|
|
# one stray comment between them silently truncates the command and the
|
|
# picker draws with two binds instead of seven. Dump argv one argument per
|
|
# line -- a bind that lost its continuation cannot masquerade as one that
|
|
# survived -- and behave like an accept: read the piped rows, print nothing.
|
|
case ${1:-} in --version) echo "${FAKE_FZF_VERSION:-9.9.9} (fake)"; exit 0 ;; esac
|
|
[ -n "${FAKE_FZF_ARGV:-}" ] && printf '%s\n' "$@" > "$FAKE_FZF_ARGV"
|
|
cat > /dev/null
|
|
exit "${FAKE_FZF_RC:-0}" ;;
|
|
esac
|
|
exit 0
|