#!/bin/bash
#
# Run continuous integration tests.
#
# Copyright (C) 2014 Red Hat
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
set -o nounset -o pipefail -o errexit
declare -r CI_DIR=`dirname "\`readlink -f \"\$0\"\`"`
export PATH=$CI_DIR:$PATH
export LC_ALL=C
. deps.sh
. distro.sh
. configure.sh
. misc.sh
declare -r DEBUG_CFLAGS="-g3 -O2"
declare -r COVERAGE_CFLAGS="-g3 -O0 --coverage"
declare -r ARCH=`uname -m`
declare -r CPU_NUM=`getconf _NPROCESSORS_ONLN`
declare -r TITLE_WIDTH=24
declare -r RESULT_WIDTH=18
# Minimum percentage of code lines covered by tests
declare -r COVERAGE_MIN_LINES=15
# Minimum percentage of code functions covered by tests
declare -r COVERAGE_MIN_FUNCS=0
declare BASE_PFX=""
declare DEPS=true
declare BASE_DIR=`pwd`
declare MODERATE=false
declare RIGOROUS=false
# Output program usage information.
function usage()
{
cat < "$log"
duration=$((end - start))
if [ "$status" == 0 ]; then
printf 'success '
else
printf 'failure '
fi
printf "%02u:%02u:%02u " \
$((duration / (60 * 60))) \
$((duration / 60 % 60)) \
$((duration % 60))
disppath "$log"
printf "\n"
return "$status"
}
# Execute mock as is, or, if the user is not in the "mock" group, under sudo,
# which has password prompt/input on the console, instead of stderr/stdin.
# Args: [mock_arg...]
function mock_privileged()
{
if memberof mock; then
mock "$@"
else
declare prompt=$'Not a "mock" group member.\n'
prompt+="To run mock enter sudo password for $USER: "
sudo -p "$prompt" mock "$@"
fi
}
# Execute mock_privileged with extra chroot configuration added.
# Args: chroot [mock_arg...]
# Input: extra configuration
function mock_privileged_conf()
{
declare -r chroot="$1"; shift
declare conf_dir
conf_dir=`mktemp --tmpdir --directory mock-config.XXXXXXXX`
trap 'trap - RETURN; rm -R "$conf_dir";' RETURN
# Preserve timestamps to avoid unnecessary cache rebuilds
cp -r --preserve=timestamps /etc/mock/* "$conf_dir"/
cat >> "${conf_dir}/${chroot}.cfg"
touch --reference="/etc/mock/${chroot}.cfg" "${conf_dir}/${chroot}.cfg"
mock_privileged --configdir="$conf_dir" --root="$chroot" "$@"
}
# Execute mock_privileged with dependency package source configuration added.
# Args: chroot [mock_arg...]
function mock_privileged_deps()
{
declare -r chroot_name="$1"; shift
declare -r config=$(basename $(readlink -f "/etc/mock/${chroot_name}.cfg"))
declare -r chroot="${config%.cfg}"
declare repo
if [[ "$chroot" == fedora-* ]]; then
repo='fedora-$releasever-$basearch'
elif [[ "$chroot" =~ epel-([0-9]+) ]]; then
repo="epel-${BASH_REMATCH[1]}-\$basearch"
else
echo "Unknown chroot config: $chroot" >&2
exit 1
fi
mock_privileged_conf "$chroot" "$@" <<<"
config_opts['yum.conf'] += '''
[sssd-deps]
name=Extra SSSD dependencies
baseurl=http://copr-be.cloud.fedoraproject.org/results/lslebodn/sssd-deps/$repo/
skip_if_unavailable=true
gpgcheck=0
enabled=1
'''
"
}
# Run debug build checks.
function build_debug()
{
export CFLAGS="$DEBUG_CFLAGS"
declare test_dir
declare test_dir_distcheck
declare intgcheck_configure_args
declare distcheck_configure_args
declare status
test_dir=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
stage configure "$BASE_DIR/configure" \
"${CONFIGURE_ARG_LIST[@]}" \
--with-test-dir="$test_dir"
# Not building "tests" due to https://fedorahosted.org/sssd/ticket/2350
stage make-tests make-check-wrap -j $CPU_NUM check -- true
status=0
CK_FORK=no \
stage make-check-valgrind \
make-check-wrap -j $CPU_NUM check -- \
libtool --mode=execute \
valgrind-condense 99 \
'!(*.py|*dlopen-tests)' -- \
--trace-children=yes \
--trace-children-skip='*/bin/*,*/sbin/*' \
--leak-check=full \
--gen-suppressions=all \
--suppressions="$CI_DIR/sssd.supp" \
--verbose ||
status=$?
mv "$test_dir" ci-test-dir
((status == 0))
if "$MODERATE"; then
if "$DEPS_INTGCHECK_SATISFIED"; then
printf -v intgcheck_configure_args " %q" \
"${CONFIGURE_ARG_LIST[@]}"
stage make-intgcheck make -j $CPU_NUM intgcheck \
INTGCHECK_CONFIGURE_FLAGS=" \
$intgcheck_configure_args"
fi
test_dir_distcheck=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
# Single thread due to https://fedorahosted.org/sssd/ticket/2354
status=0
printf -v distcheck_configure_args " %q" \
"${CONFIGURE_ARG_LIST[@]}" \
"--with-test-dir=$test_dir_distcheck"
stage make-distcheck make distcheck \
AUX_DISTCHECK_CONFIGURE_FLAGS=" \
$distcheck_configure_args" ||
status=$?
mv "$test_dir_distcheck" ci-test-dir-distcheck
((status == 0))
if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
stage make-srpm env -u CFLAGS -- make srpm
stage mock-build mock_privileged_deps "default" \
--resultdir ci-mock-result \
rpmbuild/SRPMS/*.src.rpm
fi
fi
unset CFLAGS
}
# Run coverage build checks.
function build_coverage()
{
declare -r coverage_report_dir="ci-report-coverage"
declare test_dir
export CFLAGS="$COVERAGE_CFLAGS"
test_dir=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
stage configure "$BASE_DIR/configure" \
"${CONFIGURE_ARG_LIST[@]}" \
--with-test-dir="$test_dir"
# Build everything, including tests
# Not building "tests" due to https://fedorahosted.org/sssd/ticket/2350
stage make-tests make-check-wrap -j $CPU_NUM check -- true
stage lcov-pre lcov --capture --initial --directory . \
--base-directory "$BASE_DIR" \
--output-file ci-base.info
# Run tests
stage make-check make -j $CPU_NUM check || true
mv "$test_dir" ci-test-dir
stage lcov-post lcov --capture --directory . \
--base-directory "$BASE_DIR" \
--output-file ci-check.info
stage lcov-merge lcov --add-tracefile ci-base.info \
--add-tracefile ci-check.info \
--output-file ci-dirty.info
stage lcov-clean lcov --remove ci-dirty.info \
"/usr/*" "src/tests/*" \
--output-file ci.info
stage genhtml eval 'genhtml --output-directory \
"$coverage_report_dir" \
--title "sssd" --show-details \
--legend --prefix "$BASE_DIR" \
ci.info |& tee ci-genhtml.out'
printf "%-$((TITLE_WIDTH + RESULT_WIDTH))s%s\n" \
"coverage report:" \
"`disppath \"\$coverage_report_dir/index.html\"`"
stage lcov-check eval 'lcov_check "$COVERAGE_MIN_LINES" \
"$COVERAGE_MIN_FUNCS" \
< ci-genhtml.out'
unset CFLAGS
}
# Run a build inside a sub-directory.
# Args: id cmd [arg...]
function run_build()
{
declare -r id="$1"; shift
declare -r dir="ci-build-$id"
mkdir "$dir"
printf "%-$((TITLE_WIDTH + RESULT_WIDTH))s%s\n" \
"${id^^} BUILD:" "`disppath \"\$dir\"`"
cd "$dir"
"$@"
cd ..
}
#
# Main routine
#
declare args_expr
args_expr=`getopt --name \`basename "\$0"\` \
--options hp:nemrf \
--longoptions help,prefix:,no-deps \
--longoptions essential,moderate,rigorous,full \
-- "$@"`
eval set -- "$args_expr"
while true; do
case "$1" in
-h|--help)
usage; exit 0;;
-p|--prefix)
BASE_PFX="$2"; shift 2;;
-n|--no-deps)
DEPS=false; shift;;
-e|--essential)
MODERATE=false; RIGOROUS=false; shift;;
-m|--moderate)
MODERATE=true; RIGOROUS=false; shift;;
-r|--rigorous|-f|--full)
MODERATE=true; RIGOROUS=true; shift;;
--)
shift; break;;
*)
echo "Unknown option: $1" >&2
exit 1;;
esac
done
if [ $# != 0 ]; then
echo "Positional arguments are not accepted." >&2
usage >&2
exit 1
fi
trap 'echo FAILURE' EXIT
rm_rf_ro ci-*
export V=1
if "$DEPS"; then
stage install-deps deps_install
fi
stage autoreconf autoreconf --install --force
run_build debug build_debug
if "$RIGOROUS"; then
run_build coverage build_coverage
fi
unset V
trap - EXIT
echo SUCCESS