Import LinuxCNC source tree
This commit is contained in:
394
scripts/runtests.in
Executable file
394
scripts/runtests.in
Executable file
@@ -0,0 +1,394 @@
|
||||
#!/bin/bash
|
||||
|
||||
# When running with "-v", the test itself runs in a pipeline with tee, and
|
||||
# without pipefail we get the exit value from tee instead of from the test.
|
||||
set -o pipefail
|
||||
|
||||
# The linuxcnc starter script sometimes tries to display X windows if
|
||||
# DISPLAY is set. We never want that while running tests, so unset it.
|
||||
unset DISPLAY
|
||||
|
||||
# Some of our tests emit locale-sensitive strings, so reset the locale
|
||||
# to a sane default.
|
||||
export LC_ALL=C
|
||||
export LANGUAGES=
|
||||
|
||||
TOPDIR="$(realpath "$(dirname "$0")/..")"
|
||||
|
||||
# $prefix is used in the substitutions of @...@ strings below
|
||||
# shellcheck disable=SC2034
|
||||
prefix=@prefix@
|
||||
# Shellcheck does not know about substitutions
|
||||
# shellcheck disable=SC2050
|
||||
if [ "@RUN_IN_PLACE@" = yes ]; then
|
||||
. "$TOPDIR/scripts/rip-environment" >&/dev/null
|
||||
export HEADERS=@EMC2_HOME@/include
|
||||
export LIBDIR=${TOPDIR}/lib
|
||||
export REALTIME=realtime
|
||||
else
|
||||
# Set $EMC2_HOME to $prefix for tests that depend on it
|
||||
export SYSTEM_BUILD=1
|
||||
export EMC2_HOME=@EMC2_HOME@
|
||||
export HEADERS=@includedir@/linuxcnc
|
||||
export LIBDIR=@EMC2_HOME@/lib
|
||||
export LINUXCNC_EMCSH=@WISH@
|
||||
export REALTIME=@REALTIME@
|
||||
export SUDO=sudo
|
||||
fi
|
||||
export PYTHON_CPPFLAGS="@PYTHON_CPPFLAGS@"
|
||||
export PYTHON_EXTRA_LIBS="@PYTHON_EXTRA_LIBS@"
|
||||
export PYTHON_EXTRA_LDFLAGS="@PYTHON_EXTRA_LDFLAGS@"
|
||||
export PYTHON_LIBS="@PYTHON_EXTRA_LIBS@"
|
||||
|
||||
RUNTESTS="$(readlink -f "$0")"
|
||||
export RUNTESTS
|
||||
|
||||
NUM=0
|
||||
FAIL=0; FAIL_NAMES=""
|
||||
XFAIL=0
|
||||
SHMERR=0
|
||||
SKIP=0
|
||||
VERBOSE=0
|
||||
|
||||
clean () {
|
||||
find "$*" \( -name "stderr" -or -name "result" \
|
||||
-or -name "*.var" -or -name "*.var.bak" \) \
|
||||
-print0 | xargs -0 rm -f
|
||||
}
|
||||
|
||||
wait_for_result_close() {
|
||||
# Test for the 'result' and 'stderr' files in the current testdir to be
|
||||
# closed. The 'checkresult' script cannot be run if these files are still
|
||||
# in use and would cause a race condition.
|
||||
# This function should be called with the test's directory as CWD.
|
||||
presult="$(realpath -e -q "./result")"
|
||||
pstderr="$(realpath -e -q "./stderr")"
|
||||
if [ -z "$presult" ] || [ -z "$pstderr" ]; then
|
||||
echo "Internal error: Missing 'result' or 'stderr' in wait_for_result_close()"
|
||||
exit 2
|
||||
fi
|
||||
timeoutcnt=0
|
||||
while true; do
|
||||
lsof -- "$presult" > /dev/null 2>&1; resresult=$?
|
||||
lsof -- "$pstderr" > /dev/null 2>&1; resstderr=$?
|
||||
if [ $resresult -ne 0 ] && [ $resstderr -ne 0 ]; then
|
||||
# Neither 'result' nor 'stderr' are open anymore
|
||||
break
|
||||
fi
|
||||
if [ $timeoutcnt -ge 30 ]; then
|
||||
if [ $resresult -eq 0 ]; then
|
||||
echo "*** Timeout waiting for 'result' file to close"
|
||||
fi
|
||||
if [ $resstderr -eq 0 ]; then
|
||||
echo "*** Timeout waiting for 'stderr' file to close"
|
||||
fi
|
||||
echo "*** Test results may be invalid when checked."
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
timeoutcnt=$((timeoutcnt + 1))
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
run_shell_script () {
|
||||
testname=$(basename "$1")
|
||||
testdir=$(dirname "$1")
|
||||
|
||||
pushd "$testdir" > /dev/null || exit 2
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
(bash -x "$testname" | tee result) 3>&1 1>&2 2>&3 | tee stderr
|
||||
else
|
||||
bash -x "$testname" > result 2> stderr
|
||||
fi
|
||||
exitcode=$?
|
||||
wait_for_result_close
|
||||
popd > /dev/null || exit 2
|
||||
return "$exitcode"
|
||||
}
|
||||
|
||||
run_executable () {
|
||||
testname=$(basename "$1")
|
||||
testdir=$(dirname "$1")
|
||||
|
||||
pushd "$testdir" > /dev/null || exit 2
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
(./"$testname" | tee result) 3>&1 1>&2 2>&3 | tee stderr
|
||||
else
|
||||
./"$testname" > result 2> stderr
|
||||
fi
|
||||
exitcode=$?
|
||||
wait_for_result_close
|
||||
popd > /dev/null || exit 2
|
||||
return "$exitcode"
|
||||
}
|
||||
|
||||
run_without_overruns () {
|
||||
testname=$(basename "$1")
|
||||
testdir=$(dirname "$1")
|
||||
for i in $(seq 10); do
|
||||
if [ "$i" != 1 ]; then echo "--- $testdir: overrun detected in sampler, re-running test" 1>&2 ; fi
|
||||
|
||||
pushd "$testdir" > /dev/null || exit 2
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
(halrun -f "$testname" | tee result) 3>&1 1>&2 2>&3 | tee stderr
|
||||
else
|
||||
halrun -f "$testname" > result 2> stderr
|
||||
fi
|
||||
exitcode=$?
|
||||
wait_for_result_close
|
||||
popd > /dev/null || exit 2
|
||||
|
||||
if ! grep -q '^overrun$' "$testdir/result"; then return "$exitcode"; fi
|
||||
done
|
||||
echo "--- $testdir: $i overruns detected, giving up" 1>&2
|
||||
return 1
|
||||
}
|
||||
|
||||
run_test() {
|
||||
testname="$1"
|
||||
case "$testname" in
|
||||
*.hal) run_without_overruns "$testname" ;;
|
||||
*.sh) run_shell_script "$testname" ;;
|
||||
*) run_executable "$testname" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
SHMEM_KEY=( "0x00000064" "0x48414c32" "0x48484c34" "0x90280a48" "0x130cf406" "0x434c522b" )
|
||||
SHMEM_USE=( "Emc motion" "Hal" "UUID" "Rtapi" "Hal scope" "Classicladder" )
|
||||
test_shmem() {
|
||||
# Test if there are any shared memory segments left. These will
|
||||
# interfere with performing tests as we cannot be assured of a clean
|
||||
# start for each test if a segment exists.
|
||||
mapfile -t keys < <(ipcs -m | grep -Ei "^\\s*($(IFS='|'; echo "${SHMEM_KEY[*]}"))" | awk '{print $1}')
|
||||
if [ "${#keys[@]}" -gt 0 ]; then
|
||||
echo "There are one or more shared memory segments currently allocated."
|
||||
echo "This indicates that there is a LinuxCNC instance running or it"
|
||||
echo "did not cleanup before exit."
|
||||
echo "You should run 'ipcs -m' and look for the following keys to cleanup:"
|
||||
for i in "${!SHMEM_KEY[@]}"; do
|
||||
for j in "${keys[@]}"; do
|
||||
if [ "$j" = "${SHMEM_KEY[$i]}" ]; then
|
||||
echo "${SHMEM_KEY[$i]} - ${SHMEM_USE[$i]} key"
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
echo
|
||||
echo "You should remove the key(s) with 'ipcrm -M <key>' if LinuxCNC is"
|
||||
echo "not running and no processes are attached to the segment(s)."
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
test_and_remove_shmem() {
|
||||
ret=0
|
||||
for i in "${!SHMEM_KEY[@]}"; do
|
||||
# ipcs returns the following columns:
|
||||
# key shmid owner perms bytes nattch status
|
||||
read -r -a SHM < <(ipcs -m | grep -Ei "^\\s*${SHMEM_KEY[$i]}")
|
||||
if [ "${#SHM[@]}" -ge 6 ]; then
|
||||
echo "*** SHMERR: Shared memory segment ${SHMEM_KEY[$i]} (${SHMEM_USE[$i]} key) was not removed. Removing..."
|
||||
ipcrm -M "${SHMEM_KEY[$i]}"
|
||||
# Check number of attached processes. It should be zero.
|
||||
if [ "${SHM[5]}" -ne 0 ]; then
|
||||
echo "*** SHMERR: Shared memory segment ${SHMEM_KEY[$i]} has at least one attached process."
|
||||
echo "*** SHMERR: Manual intervention is required to solve the situation."
|
||||
return 2
|
||||
fi
|
||||
ret=1
|
||||
fi
|
||||
done
|
||||
return $ret
|
||||
}
|
||||
|
||||
TMPDIR=$(mktemp -d /tmp/runtest.XXXXXX)
|
||||
trap 'rm -rf "$TMPDIR"' 0 1 2 3 15
|
||||
|
||||
|
||||
run_tests () {
|
||||
if ! test_shmem; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
find "$@" -name test.hal -or -name test.sh -or -name test \
|
||||
| sort > "$TMPDIR/alltests"
|
||||
|
||||
while read -r testname; do
|
||||
testdir=$(dirname "$testname")
|
||||
# check if there's a "musthave" file with prerequisites from config.h
|
||||
if [ -e "$testdir/musthave" ] ; then
|
||||
# one prerequisite per line
|
||||
while IFS= read -r prereq ; do
|
||||
if ! grep --quiet --regexp "^#define HAVE_$prereq.*" "$TOPDIR/src/config.h" ; then
|
||||
echo "Skipping test for missing prerequisite \"$prereq\": $testdir" 1>&2
|
||||
SKIP=$((SKIP + 1))
|
||||
continue 3
|
||||
fi
|
||||
done < "$testdir/musthave"
|
||||
fi
|
||||
# skip test if there's a "skip" file
|
||||
if [ -e "$testdir/skip" ]; then
|
||||
if ! [ -x "$testdir/skip" ] || ! "$testdir/skip"; then
|
||||
echo "Skipping disabled test: $testdir" 1>&2
|
||||
SKIP=$((SKIP + 1))
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
if $NOSUDO && [ -e "$testdir/control" ] && \
|
||||
grep Restrictions: "$testdir/control" | grep -q sudo; then
|
||||
if ! [ -x "$testdir/skip" ] || ! "$testdir/skip"; then
|
||||
echo "Skipping sudo test: $testdir" 1>&2
|
||||
SKIP=$((SKIP + 1))
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
NUM=$((NUM + 1))
|
||||
TEST_DIR=$(readlink -f "$testdir")
|
||||
export TEST_DIR
|
||||
echo "Running test: $testdir" 1>&2
|
||||
if test -n "$SYSTEM_BUILD"; then
|
||||
# Tell `halcompile` where to install comps
|
||||
USER_MODULE_DIR=$(readlink -f "$testdir") \
|
||||
PATH=$(readlink -f "$testdir"):$PATH \
|
||||
run_test "$testname"
|
||||
else
|
||||
run_test "$testname"
|
||||
fi
|
||||
exitcode=$?
|
||||
if [ "$exitcode" -ne 0 ]; then
|
||||
reason="test run exited with $exitcode"
|
||||
else
|
||||
if [ -e "$testdir/checkresult" ]; then
|
||||
"$testdir/checkresult" "$testdir/result"
|
||||
exitcode=$?
|
||||
reason="checkresult exited with $exitcode"
|
||||
elif [ -f "$testdir/expected" ]; then
|
||||
cmp -s "$testdir/expected" "$testdir/result"
|
||||
exitcode=$?
|
||||
reason="result differed from expected"
|
||||
if [ "$exitcode" -ne 0 ]; then
|
||||
diff -u "$testdir/expected" "$testdir/result" > "$TMPDIR/diff"
|
||||
SIZE=$(wc -l < "$TMPDIR/diff")
|
||||
if [ "$SIZE" -lt 40 ]; then
|
||||
cat "$TMPDIR/diff"
|
||||
else
|
||||
OMIT=$((SIZE-40))
|
||||
head -40 "$TMPDIR/diff"
|
||||
echo "($OMIT more lines omitted)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
exitcode=1
|
||||
reason="Neither expected nor checkresult existed"
|
||||
fi
|
||||
fi
|
||||
if [ "$exitcode" -ne 0 ]; then
|
||||
echo "*** $testdir: XFAIL: $reason"
|
||||
if test $PRINT = 1; then
|
||||
echo "************** result:"
|
||||
tail -500 "$testdir/result" | sed 's/^/ /'
|
||||
echo "************** stderr:"
|
||||
tail -500 "$testdir/stderr" | sed 's/^/ /'
|
||||
echo "**************"
|
||||
fi
|
||||
if [ -f "$testdir/xfail" ]; then
|
||||
XFAIL=$((XFAIL + 1))
|
||||
if [ $NOCLEAN -eq 0 ]; then
|
||||
rm -f "$testdir/stderr" "$testdir/result" \
|
||||
"$testdir"/*.var "$testdir"/*.var.bak
|
||||
fi
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
FAIL_NAMES="$FAIL_NAMES"$'\n'"$testdir"
|
||||
fi
|
||||
if test $STOP = 1; then
|
||||
break
|
||||
fi
|
||||
else
|
||||
if [ -f "$testdir/xfail" ]; then
|
||||
echo "*** $testdir: XPASS: Passed, but was expected to fail"
|
||||
else
|
||||
if [ $NOCLEAN -eq 0 ]; then
|
||||
rm -f "$testdir/stderr" "$testdir/result" \
|
||||
"$testdir"/*.var "$testdir"/*.var.bak
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test_and_remove_shmem; then
|
||||
if [ $? -eq 2 ]; then
|
||||
# Cannot remove attached segments. Fail hard.
|
||||
exit 1
|
||||
fi
|
||||
SHMERR=$((SHMERR + 1))
|
||||
fi
|
||||
done < "$TMPDIR/alltests"
|
||||
|
||||
SUCC=$((NUM-FAIL-XFAIL))
|
||||
echo "Runtest: $NUM tests run, $SUCC successful, $FAIL failed + $XFAIL expected, $SKIP skipped, $SHMERR shmem errors"
|
||||
if [ "$FAIL" -ne 0 ]; then
|
||||
echo "Failed: $FAIL_NAMES"
|
||||
exit 1;
|
||||
else
|
||||
exit 0;
|
||||
fi
|
||||
}
|
||||
|
||||
usage () {
|
||||
P=${0##*/}
|
||||
cat <<EOF
|
||||
$P: Run HAL test suite items
|
||||
|
||||
Usage:
|
||||
$P [-n] [-s] [-p] tests
|
||||
Run tests. With '-n', do not remove temporary files for successful
|
||||
tests. With '-s', stop after any failed test. With '-p', print
|
||||
stderr and result files.
|
||||
|
||||
$P -c tests
|
||||
Remove temporary files from an earlier test run.
|
||||
|
||||
$P -u
|
||||
Only run tests that require normal user access. Skip tests
|
||||
requiring root or sudo.
|
||||
|
||||
$P -v
|
||||
Show stdout and stderr (normally it's hidden).
|
||||
EOF
|
||||
}
|
||||
|
||||
CLEAN_ONLY=0
|
||||
NOCLEAN=0
|
||||
NOSUDO=false
|
||||
STOP=0
|
||||
PRINT=0
|
||||
while getopts cnuvsph opt; do
|
||||
case "$opt" in
|
||||
c) CLEAN_ONLY=1 ;;
|
||||
n) NOCLEAN=1 ;;
|
||||
u) NOSUDO=true ;;
|
||||
v) VERBOSE=1 ;;
|
||||
s) STOP=1 ;;
|
||||
p) PRINT=1 ;;
|
||||
h|?) usage; exit 0 ;;
|
||||
*) usage; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
if [ -f test.hal ] || [ -f test.sh ]; then
|
||||
set -- .
|
||||
else
|
||||
set -- "$TOPDIR/tests"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $CLEAN_ONLY -eq 1 ]; then
|
||||
clean "$@"
|
||||
else
|
||||
run_tests "$@"
|
||||
fi
|
||||
Reference in New Issue
Block a user