#!/bin/bash

## Copyright (C) 2025 - 2026 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## Runs the privleap regression suite under coverage.py and reports how much of
## the privleap Python code it reaches.

set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose
export LC_ALL=C

repo_dir="$(pwd)"
rcfile="${repo_dir}/debian/tests/coverage/coveragerc"

## AUTOPKGTEST_ARTIFACTS might be a relative path.
artifacts_dir="$(realpath -- "${AUTOPKGTEST_ARTIFACTS:-${repo_dir}/autopkgtest-artifacts}")"

## Create a coverage directory that root reads from or writes into.
make_cov_dir() {
  if ! mkdir -- "${1}" 2>/dev/null; then
    printf 'ERROR: coverage dir pre-exists or is not creatable: %s\n' "${1}" 1>&2
    exit 1
  fi
  chmod "${2}" -- "${1}"
}

## cov_dir holds raw coverage data and must be world-writable for the sake of
## unprivileged clients. Sticky bit prevents undesired deletion.
cov_dir="${artifacts_dir}/privleap-coverage"
make_cov_dir "${cov_dir}" 1777

## sitecustomize.py and coveragerc must be readable by unprivileged clients.
cov_bootstrap_dir="${artifacts_dir}/privleap-coverage-bootstrap"
make_cov_dir "${cov_bootstrap_dir}" 0755
cp -- "${repo_dir}/debian/tests/coverage/sitecustomize.py" \
  "${cov_bootstrap_dir}/"
cp -- "${rcfile}" "${cov_bootstrap_dir}/"
chmod 644 -- "${cov_bootstrap_dir}/sitecustomize.py" \
  "${cov_bootstrap_dir}/coveragerc"
rcfile="${cov_bootstrap_dir}/coveragerc"

## All coverage-activating environment variables must survive 'sudo -u'.
coverage_sudoers=/etc/sudoers.d/privleap-coverage
coverage_sudoers_tmp="$(mktemp)"
trap 'rm -f -- "${coverage_sudoers}" "${coverage_sudoers_tmp}"' EXIT
trap 'exit 130' INT TERM ## triggers cleanup without preventing exit
printf '%s\n' \
  'Defaults:root env_keep += "COVERAGE_PROCESS_START"' \
  'Defaults:root env_keep += "COVERAGE_FILE"' \
  'Defaults:root env_keep += "PYTHONPATH"' \
  > "${coverage_sudoers_tmp}"
visudo -cf "${coverage_sudoers_tmp}" >/dev/null
install -m 0440 -o root -g root -- \
  "${coverage_sudoers_tmp}" "${coverage_sudoers}"

export COVERAGE_PROCESS_START="${rcfile}"
export COVERAGE_FILE="${cov_dir}/.coverage"
export PYTHONPATH="${cov_bootstrap_dir}${PYTHONPATH:+:${PYTHONPATH}}"

test_rc=0
./test/run-test || test_rc="$?"

## Make sure some non-root-owned coverage files exist.
client_cov_count="$(find "${cov_dir}" -maxdepth 1 -type f -name '.coverage.*' \
  -not -user root -printf '.' | wc -c)"
if [ "${client_cov_count}" -eq 0 ]; then
  printf '%s\n' 'ERROR: no coverage data from unprivileged clients was captured, coverage data is incomplete.' >&2
  exit 1
fi
printf '%s\n' "Captured coverage from ${client_cov_count} unprivileged client process(es)." >&2

## Lock down and cleanup cov_dir before doing file combination.
chmod 0700 -- "${cov_dir}"
find "${cov_dir}" -maxdepth 1 -type f '(' -not -name '.coverage.*' \
  -and -not -name '.coverage' ')' -delete
find "${cov_dir}" -maxdepth 1 -type l -delete

python3 -m coverage combine --rcfile="${rcfile}" "${cov_dir}"
python3 -m coverage report --rcfile="${rcfile}" 1>&2
python3 -m coverage xml --rcfile="${rcfile}" -o "${cov_dir}/coverage.xml"
python3 -m coverage html --rcfile="${rcfile}" --directory="${cov_dir}/coverage_html"

exit "${test_rc}"
