#!/usr/bin/env bats load test_helper # Each test runs the real update script from a fake CoreDesktop checkout in the # test's HOME, with fake hooks in updates.d/ that log to $RAN when they run. setup() { # A checkout with nothing but the update script and the files it installs common_setup CD="$HOME/.local/share/coredesktop" mkdir -p "$CD/updates.d" "$HOME/.local/bin" cp "$REPO/update" "$CD/update" for script in refresh sibling version configure audit; do echo '#!/bin/bash' > "$CD/$script" done export RAN="$BATS_TEST_TMPDIR/ran" unset UPDATE_REEXECED UPDATE_PULL_FAILED UPDATE_FORCE stub clear stub git 'exit "${GIT_STATUS:-0}"' stub sudo 'exit "${SUDO_STATUS:-0}"' stub df 'printf "Avail\n%s\n" "${DF_AVAIL:-104857600}"' # Let go of bats' output pipes so the sudo keepalive's leftover sleep # doesn't keep the test waiting stub sleep 'exec > /dev/null 2>&1 3>&-; exec /usr/bin/sleep "$@"' } # hook NAME BODY: add updates.d/NAME.sh with BODY as its run_update hook() { printf '#!/bin/bash\nrun_update() {\n%s\n}\n' "$2" > "$CD/updates.d/$1.sh" } ran() { [ "$(cat "$RAN" 2> /dev/null)" = "$(printf '%s\n' "$@")" ] } @test "update pulls, installs its scripts, and runs every hook in order" { hook 20-b 'echo b >> "$RAN"' hook 10-a 'echo a >> "$RAN"' run "$CD/update" [ "$status" -eq 0 ] [[ "$output" == *"Done."* ]] called git -C "$CD" pull --ff-only ran a b for script in update refresh sibling version configure audit; do [ -x "$HOME/.local/bin/$script" ] done } @test "update pulls only once, before re-running its new copy" { hook 10-a 'echo a >> "$RAN"' run "$CD/update" [ "$status" -eq 0 ] [ "$(grep -c '^git ' "$STUB_LOG")" -eq 1 ] ran a } @test "a failing hook doesn't stop the hooks after it" { hook 10-a 'echo a >> "$RAN"' hook 20-b 'echo b >> "$RAN"; return 1' hook 30-c 'echo c >> "$RAN"' run "$CD/update" [ "$status" -eq 1 ] [[ "$output" == *"Done, with errors in: 20-b.sh"* ]] ran a b c } @test "a hook that exits doesn't end the update" { hook 10-a 'echo a >> "$RAN"; exit 0' hook 20-b 'echo b >> "$RAN"' run "$CD/update" [ "$status" -eq 0 ] ran a b } @test "a hook using set -e fails at its first failing command" { hook 10-a 'set -e; echo a >> "$RAN"; false; echo after >> "$RAN"' hook 20-b 'echo b >> "$RAN"' run "$CD/update" [ "$status" -eq 1 ] [[ "$output" == *"errors in: 10-a.sh"* ]] ran a b } @test "a hook's is_installed and functions don't leak into the next hook" { printf '%s\n' '#!/bin/bash' 'is_installed() { false; }' \ 'run_update() { echo a >> "$RAN"; }' > "$CD/updates.d/10-a.sh" # 10-a's is_installed must not stop 20-b, and its run_update must not run # again for 30-c, which has none printf '%s\n' '#!/bin/bash' 'run_update() { echo b >> "$RAN"; }' \ > "$CD/updates.d/20-b.sh" printf '%s\n' '#!/bin/bash' 'true' > "$CD/updates.d/30-c.sh" run "$CD/update" [ "$status" -eq 0 ] ran b } @test "update runs the hooks on disk when git pull fails, and reports it" { export GIT_STATUS=1 hook 10-a 'echo a >> "$RAN"' run "$CD/update" [ "$status" -eq 1 ] [[ "$output" == *"could not pull"* ]] [[ "$output" == *"errors in: git pull"* ]] ran a } @test "update stops before any hook when / is low on space" { export DF_AVAIL=1048576 hook 10-a 'echo a >> "$RAN"' run "$CD/update" [ "$status" -eq 1 ] [[ "$output" == *"needs at least 10 GiB"* ]] [ ! -e "$RAN" ] } @test "UPDATE_FORCE skips the free space check" { export DF_AVAIL=1048576 UPDATE_FORCE=1 hook 10-a 'echo a >> "$RAN"' run "$CD/update" [ "$status" -eq 0 ] ran a } @test "update stops when sudo fails" { export SUDO_STATUS=1 hook 10-a 'echo a >> "$RAN"' run "$CD/update" [ "$status" -eq 1 ] [ ! -e "$RAN" ] }