1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2026-10-29 15:02:30 +00:00
Files
go-chasquid-smtp/test/util/lib.sh
T
Alberto Bertogli 9b3f7ca193 test: Wait for children to exit before finishing an integration test
Some integration tests start servers on predictable ports, and then the
servers are killed at exit.

However, we currently don't want for them to die. This can cause races
where the next test starts while the previous daemons are still in the
process of exiting, causing flakiness.

This patch fixes the problem by waiting for the child processes to exit
before exiting the test.
2026-10-03 17:35:56 +01:00

259 lines
6.5 KiB
Bash

#!/bin/bash
# Library to write the shell scripts in the tests.
function init() {
if [ "$V" == "1" ]; then
set -v
fi
UTILDIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")" )
export UTILDIR
TBASE=$(realpath "$(dirname "$0")" )
cd "${TBASE}" || exit 1
if [ "${RACE}" == "1" ]; then
GOFLAGS="$GOFLAGS -race"
fi
if [ "${GOCOVERDIR}" != "" ]; then
GOFLAGS="$GOFLAGS -cover -covermode=count"
fi
# Remove the directory where test-mda will deliver mail, so previous
# runs don't interfere with this one.
rm -rf .mail
# Set traps to kill our subprocesses when we exit (for any reason).
trap ":" TERM # Avoid the EXIT handler from killing bash.
trap "exit 2" INT # Ctrl-C, make sure we fail in that case.
# Kill children on exit, and wait for them to finish, so they don't
# interfere with the next test (e.g. by still listening on a port).
# "wait" returns early (and with an error) if we get a signal while
# waiting, so we loop until it completes.
trap "kill 0; until wait; do :; done" EXIT
}
function chasquid() {
go-build-cached "${TBASE}/../../"
# HOSTALIASES: so we "fake" hostnames.
# PATH: so chasquid can call test-mda without path issues.
# MDA_DIR: so our test-mda knows where to deliver emails.
HOSTALIASES=${TBASE}/hosts \
PATH=${UTILDIR}:${PATH} \
MDA_DIR=${TBASE}/.mail \
"${TBASE}/../../chasquid" "$@"
}
function go-build-cached() { (
# This runs "go build" on the given directory, but only once every
# 10s, or if the build flags/tags change.
# Because in tests we run some of the Go programs often, this speeds
# up the tests.
cd "$1" || exit 1
touch -d "10 seconds ago" .reference
echo "-tags=$GOTAGS : $GOFLAGS" > .flags-new
if
! cmp -s .flags-new .flags >/dev/null 2>&1 ||
[ "$(basename "$PWD")" -ot ".reference" ] ;
then
# Build to a temporary file first, then atomically rename it.
# This prevents "text file busy" errors when the binary is
# executed while being written to.
BINARY="$(basename "$PWD")"
TMPFILE=".${BINARY}.tmp.${RANDOM}"
# shellcheck disable=SC2086
if go build -tags="$GOTAGS" $GOFLAGS -o "$TMPFILE"; then
# Atomically replace the binary.
mv "$TMPFILE" "$BINARY"
else
# Clean up the temp file if the build failed.
rm -f "$TMPFILE"
exit 1
fi
# Write to .flags instead of renaming, to prevent races where
# was .flags-new is already renamed by the time we get here.
# Do this _after_ the build so worst case we build twice,
# instead of having the chance to run an old binary.
echo "-tags=$GOTAGS : $GOFLAGS" > .flags
fi
) }
function chasquid-util() {
# Run chasquid-util from inside the config dir, since in our tests
# data_dir is relative to the config.
go-build-cached "${TBASE}/../../cmd/chasquid-util/"
CONFDIR="${CONFDIR:-config}"
( cd "$CONFDIR" && \
"${TBASE}/../../cmd/chasquid-util/chasquid-util" \
-C=. \
"$@" \
)
}
# Add a user with chasquid-util. Because this is somewhat cryptographically
# intensive, it can slow down the tests significantly, so most of the time we
# use the simpler add_user (below) for testing purposes.
function chasquid-util-user-add() {
CONFDIR="${CONFDIR:-config}"
DOMAIN=$(echo "$1" | cut -d @ -f 2)
mkdir -p "${CONFDIR}/domains/$DOMAIN/"
chasquid-util \
user-add "$1" \
--password="$2" \
>> .add_user_logs
}
function add_user() {
CONFDIR="${CONFDIR:-config}"
USERNAME=$(echo "$1" | cut -d @ -f 1)
DOMAIN=$(echo "$1" | cut -d @ -f 2)
USERDB="${CONFDIR}/domains/$DOMAIN/users"
mkdir -p "${CONFDIR}/domains/$DOMAIN/"
if ! [ -f "${USERDB}" ] || ! grep -E -q "key:.*${USERNAME}" "${USERDB}"; then
echo "users:{ key: '${USERNAME}' value:{ plain:{ password: '$2' }}}" \
>> "${USERDB}"
fi
}
function dovecot-auth-cli() {
go-build-cached "${TBASE}/../../cmd/dovecot-auth-cli/"
"${TBASE}/../../cmd/dovecot-auth-cli/dovecot-auth-cli" "$@"
}
function run_msmtp() {
# msmtp will check that the rc file is only user readable.
chmod 600 msmtprc
# msmtp binary is often g+s, which causes $HOSTALIASES to not be
# honoured, which breaks the tests. Copy the binary to remove the
# setgid bit as a workaround.
cp -u "$(command -v msmtp)" "${UTILDIR}/.msmtp-bin"
HOSTALIASES=${TBASE}/hosts \
"${UTILDIR}/.msmtp-bin" -C msmtprc "$@"
}
function mail_diff() {
"${UTILDIR}/mail_diff" "$@"
}
function chamuyero() {
"${UTILDIR}/chamuyero" "$@"
}
function generate_cert() {
go-build-cached "${UTILDIR}/generate_cert/"
"${UTILDIR}/generate_cert/generate_cert" "$@"
}
function loadgen() {
go-build-cached "${UTILDIR}/loadgen/"
"${UTILDIR}/loadgen/loadgen" "$@"
}
function conngen() {
go-build-cached "${UTILDIR}/conngen/"
"${UTILDIR}/conngen/conngen" "$@"
}
function minidns_bg() {
go-build-cached "${UTILDIR}/minidns/"
"${UTILDIR}/minidns/minidns" "$@" &
export MINIDNS=$!
}
function fexp() {
go-build-cached "${UTILDIR}/fexp/"
"${UTILDIR}/fexp/fexp" "$@"
}
function smtpc() {
go-build-cached "${UTILDIR}/smtpc/"
"${UTILDIR}/smtpc/smtpc" "$@"
}
function timeout() {
MYPID=$$
(
sleep "$1"
echo "timed out after $1, killing test"
kill -9 $MYPID
) &
}
function success() {
echo success
}
function skip() {
echo "skipped: $*"
exit 0
}
function fail() {
echo "FAILED: $*"
exit 1
}
function check_hostaliases() {
if ! "${UTILDIR}/check-hostaliases"; then
skip "\$HOSTALIASES not working (probably systemd-resolved)"
fi
}
# Wait until there's something listening on the given port.
function wait_until_ready() {
PORT=$1
while ! bash -c "true < /dev/tcp/localhost/$PORT" 2>/dev/null ; do
sleep 0.01
done
}
# Wait for the given file to exist.
function wait_for_file() {
while ! [ -e "$1" ]; do
sleep 0.01
done
}
function wait_until() {
while true; do
if eval "$*"; then
return 0
fi
sleep 0.01
done
}
# Generate certs for the given hostname.
function generate_certs_for() {
CONFDIR="${CONFDIR:-config}"
# Generating certs is takes time and slows the tests down, so we keep
# a little cache that is common to all tests.
CACHEDIR="${TBASE}/../.generate_certs_cache"
mkdir -p "${CACHEDIR}/$1/"
touch -d "10 minutes ago" "${CACHEDIR}/.reference"
if [ "${CACHEDIR}/$1/privkey.pem" -ot "${CACHEDIR}/.reference" ]; then
# Cache miss (either was not there, or was too old).
(
cd "${CACHEDIR}/$1/" || exit 1
generate_cert -ca -validfor=1h -host="$1"
)
fi
mkdir -p "${CONFDIR}/certs/$1/"
cp -p "${CACHEDIR}/$1"/* "${CONFDIR}/certs/$1/"
}
function chasquid_ram_peak() {
# Find the pid of the daemon, which we expect is running on the
# background somewhere within our current session.
SERVER_PID=$(pgrep -s 0 -x chasquid)
grep VmHWM "/proc/$SERVER_PID/status" | cut -d ':' -f 2-
}