Skip to content

Commit bc22e81

Browse files
authored
Merge pull request #6447 from victormlg/cf-reactor.RunTestsInJenkins
Added reactor tests to make check
2 parents 6734d28 + 045402d commit bc22e81

13 files changed

Lines changed: 294 additions & 109 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ stamp-h1
5858
/tests/acceptance/25_cf-execd/Makefile
5959
/tests/acceptance/Makefile
6060
/tests/load/Makefile
61+
/tests/reactor/Makefile
6162
/tests/unit/Makefile
6263
/tests/static-check/Makefile
6364
/tests/valgrind-check/Makefile

‎configure.ac‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2032,6 +2032,7 @@ AC_CONFIG_FILES([Makefile
20322032
tests/acceptance/25_cf-execd/Makefile
20332033
tests/unit/Makefile
20342034
tests/load/Makefile
2035+
tests/reactor/Makefile
20352036
tests/static-check/Makefile
20362037
tests/valgrind-check/Makefile
20372038
tests/asan-check/Makefile])

‎tests/Makefile.am‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@
2121
# (COSL) may apply to this file if you as a licensee so wish it. See
2222
# included file COSL.txt.
2323
#
24-
SUBDIRS = unit load acceptance static-check valgrind-check asan-check
24+
SUBDIRS = unit load acceptance reactor static-check valgrind-check asan-check

‎tests/reactor/Makefile.am‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#
2+
# Copyright 2026 Northern.tech AS
3+
#
4+
# This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5+
#
6+
# This program is free software; you can redistribute it and/or modify it
7+
# under the terms of the GNU General Public License as published by the
8+
# Free Software Foundation; version 3.
9+
#
10+
# This program is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with this program; if not, write to the Free Software
17+
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18+
#
19+
# To the extent this program is licensed as part of the Enterprise
20+
# versions of CFEngine, the applicable Commercial Open Source License
21+
# (COSL) may apply to this file if you as a licensee so wish it. See
22+
# included file COSL.txt.
23+
#
24+
25+
# The test scripts, with their policy next to them
26+
REACTOR_TEST_SH = $(sort $(wildcard \
27+
$(srcdir)/tests/*/*.sh \
28+
$(srcdir)/tests/*/unsafe/*.sh))
29+
REACTOR_TEST_CF = $(REACTOR_TEST_SH:.sh=.cf)
30+
31+
# cf-reactor is not built with builtin extensions (e.g. on Windows)
32+
if !BUILTIN_EXTENSIONS
33+
TESTS = $(patsubst $(srcdir)/%,%,$(REACTOR_TEST_SH))
34+
endif
35+
36+
# Each test is run by run_test.sh, with the binaries of this build. The ones
37+
# in an unsafe/ directory are skipped unless UNSAFE_TESTS=1.
38+
TESTS_ENVIRONMENT = env \
39+
CF_REACTOR=$(abs_top_builddir)/cf-reactor/cf-reactor \
40+
CF_PROMISES=$(abs_top_builddir)/cf-promises/cf-promises \
41+
$(SHELL) $(srcdir)/run_test.sh
42+
43+
EXTRA_DIST = lib.cf lib.sh run.sh run_test.sh \
44+
$(patsubst $(srcdir)/%,%,$(REACTOR_TEST_SH) $(REACTOR_TEST_CF))

‎tests/reactor/lib.sh‎

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,89 @@
11
# Helpers sourced by the tests. Expects CF_REACTOR and LOG to be exported by
2-
# run.sh.
2+
# run_test.sh.
3+
#
4+
# Only POSIX tools are used, as the tests are run on all the platforms (e.g.
5+
# no timeout(1), grep -m or date +%s).
6+
7+
# Seconds to wait for something before giving up
8+
WAIT_TIMEOUT=60
39

410
# number of log lines already consumed by wait_for_log
511
log_offset=0
612

13+
# Run command $@ until it succeeds, for at most WAIT_TIMEOUT seconds, polling
14+
# every 0.1 second, or every second where sleep only takes whole seconds.
15+
# Fails if it never succeeded. Runs in a subshell without tracing, so that
16+
# set -x doesn't log every poll.
17+
wait_until() {
18+
(
19+
set +x
20+
if sleep 0.1 2>/dev/null; then
21+
interval=0.1 polls=$((WAIT_TIMEOUT * 10))
22+
else
23+
interval=1 polls=$WAIT_TIMEOUT
24+
fi
25+
until "$@"; do
26+
[ "$polls" -gt 0 ] || exit 1
27+
polls=$((polls - 1))
28+
sleep "$interval"
29+
done
30+
)
31+
}
32+
33+
# Print the current time in seconds since the Epoch. date +%s is not POSIX;
34+
# without it, srand() seeds with the time of day and returns the last seed.
35+
now() {
36+
t=$(date +%s 2>/dev/null)
37+
case "$t" in
38+
''|*[!0-9]*) awk 'BEGIN { srand(); print srand() }' ;;
39+
*) echo "$t" ;;
40+
esac
41+
}
42+
43+
# Print the first line of the reactor log after line $1 that contains $2,
44+
# prefixed with its number counted from line $1. Fails if there is none.
45+
log_match() {
46+
tail -n "+$(($1 + 1))" "$LOG" 2>/dev/null | grep -n -F -- "$2" | head -n 1 | grep .
47+
}
48+
49+
# Fail unless the reactor log contains $1.
50+
log_contains() {
51+
grep -q -F -- "$1" "$LOG" 2>/dev/null
52+
}
53+
54+
# Print the number of lines of file $1, 0 if it doesn't exist.
55+
count_lines() {
56+
if [ -f "$1" ]; then
57+
# without the padding of some wc implementations
58+
echo $(( $(wc -l < "$1") ))
59+
else
60+
echo 0
61+
fi
62+
}
63+
64+
# Fail unless file $1 has at least $2 lines.
65+
has_lines() {
66+
[ "$(count_lines "$1")" -ge "$2" ]
67+
}
68+
769
# Wait until the reactor log contains $1 in a line after the last match, then
870
# skip past that line, so that the next wait only sees newer output.
971
wait_for_log() {
10-
if ! match=$(timeout 60 sh -c '
11-
until tail -n "+$(($2 + 1))" "$1" | grep -n -F -m 1 -- "$3"; do
12-
sleep 0.1
13-
done' _ "$LOG" "$log_offset" "$1")
72+
if ! wait_until log_match "$log_offset" "$1" >/dev/null
1473
then
1574
echo "Timed out waiting for '$1' in reactor log:" >&2
1675
cat "$LOG" >&2
1776
return 1
1877
fi
78+
match=$(log_match "$log_offset" "$1")
1979
log_offset=$((log_offset + ${match%%:*}))
2080
}
2181

2282
# Wait until cf-reactor has registered the watchers of the policy, $1 of them.
2383
# The watcher thread may log before this line, so search the whole log and
2484
# leave the offset of wait_for_log as is.
2585
wait_for_watchers() {
26-
if ! timeout 60 sh -c '
27-
until grep -q -F -- "$2" "$1"; do
28-
sleep 0.1
29-
done' _ "$LOG" "verbose: cf-reactor event watcher has $1 watcher(s)"
86+
if ! wait_until log_contains "verbose: cf-reactor event watcher has $1 watcher(s)"
3087
then
3188
echo "Timed out waiting for $1 watcher(s) in reactor log:" >&2
3289
cat "$LOG" >&2
@@ -36,17 +93,14 @@ wait_for_watchers() {
3693

3794
# Wait until file $1 has at least $2 lines, then fail if it has more than $2.
3895
wait_for_lines() {
39-
if ! timeout 60 sh -c '
40-
until [ "$(cat "$1" 2>/dev/null | wc -l)" -ge "$2" ]; do
41-
sleep 0.1
42-
done' _ "$1" "$2"
96+
if ! wait_until has_lines "$1" "$2"
4397
then
4498
echo "Timed out waiting for $2 line(s) in '$1', reactor log:" >&2
4599
cat "$LOG" >&2
46100
return 1
47101
fi
48102

49-
lines=$(wc -l < "$1")
103+
lines=$(count_lines "$1")
50104
if [ "$lines" -ne "$2" ]
51105
then
52106
echo "Expected $2 line(s) in '$1', got $lines" >&2

‎tests/reactor/run.sh‎

Lines changed: 61 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2,96 +2,79 @@
22

33
# Usage: run.sh [--unsafe]
44
#
5-
# Must be run from the tests/reactor directory.
5+
# Runs all the reactor tests, see run_test.sh, and prints a summary of their
6+
# results. The output of a test is only printed if it failed. They are also
7+
# run by "make check".
68
#
79
# Tests in an unsafe/ directory modify the system they are running on, so they
8-
# are only run with --unsafe.
10+
# are skipped unless run with --unsafe (or UNSAFE_TESTS=1).
911
#
1012
# The binaries are taken from $CF_BINDIR if it is set, otherwise from the
1113
# CFEngine build tree this directory is part of if they are built, otherwise
1214
# from /var/cfengine/bin.
1315

14-
fail=0
15-
16-
unsafe=0
1716
if [ "$1" = "--unsafe" ]; then
18-
unsafe=1
17+
UNSAFE_TESTS=1
1918
fi
20-
21-
banner() {
22-
name=" $(basename "$(dirname "$1")")/$(basename "$1") "
23-
width=72
24-
left=$(( (width - ${#name}) / 2 ))
25-
right=$(( width - ${#name} - left ))
26-
[ "$left" -lt 0 ] && left=0
27-
[ "$right" -lt 0 ] && right=0
28-
printf '%s%s%s\n' \
29-
"$(printf "%${left}s" '' | tr ' ' '=')" \
30-
"$name" \
31-
"$(printf "%${right}s" '' | tr ' ' '=')"
32-
}
33-
34-
error() {
35-
echo "test '$1' FAILED" >&2
36-
fail=1
37-
}
38-
39-
# Print the path of binary $1, see the usage above.
40-
find_binary() {
41-
if [ -n "$CF_BINDIR" ]; then
42-
echo "$CF_BINDIR/$1"
43-
elif [ -x "$PWD/../../$1/$1" ]; then
44-
# in-tree build, may be a libtool wrapper script
45-
echo "$PWD/../../$1/$1"
46-
else
47-
echo "/var/cfengine/bin/$1"
48-
fi
19+
export UNSAFE_TESTS
20+
21+
dir=$(cd "$(dirname "$0")" && pwd)
22+
23+
# for now()
24+
. "$dir/lib.sh"
25+
26+
passed=0
27+
failed=0
28+
skipped=0
29+
total=0
30+
failures=""
31+
suite_start=$(now)
32+
33+
# Run test $1 and print its result, with its output if it failed
34+
run_test() {
35+
name=${1#"$dir/tests/"}
36+
name=${name%.sh}
37+
total=$((total + 1))
38+
39+
start=$(now)
40+
output=$(sh "$dir/run_test.sh" "$1" 2>&1)
41+
result=$?
42+
elapsed=$(($(now) - start))
43+
44+
case $result in
45+
0)
46+
passed=$((passed + 1))
47+
echo "--- PASS: $name (${elapsed}s)"
48+
;;
49+
77)
50+
skipped=$((skipped + 1))
51+
echo "--- SKIP: $name (${elapsed}s)"
52+
;;
53+
*)
54+
failed=$((failed + 1))
55+
failures="$failures $name
56+
"
57+
echo "--- FAIL: $name (${elapsed}s)"
58+
echo "$output"
59+
echo "---"
60+
;;
61+
esac
4962
}
5063

51-
CF_REACTOR=$(find_binary cf-reactor)
52-
CF_PROMISES=$(find_binary cf-promises)
53-
export CF_REACTOR
54-
55-
for b in "$CF_REACTOR" "$CF_PROMISES"; do
56-
if [ ! -x "$b" ]; then
57-
echo "'$b' not found, build CFEngine or set CF_BINDIR" >&2
58-
exit 2
59-
fi
60-
done
61-
echo "Using $CF_REACTOR and $CF_PROMISES"
62-
63-
for t in "$PWD"/tests/*/*.sh "$PWD"/tests/*/unsafe/*.sh; do
64+
for t in "$dir"/tests/*/*.sh "$dir"/tests/*/unsafe/*.sh; do
6465
# skip the pattern itself if there are no unsafe tests
6566
[ -f "$t" ] || continue
66-
case "$t" in
67-
*/unsafe/*) [ "$unsafe" = 1 ] || continue ;;
68-
esac
69-
70-
banner "$t"
71-
72-
# create throwaway work dir
73-
WORKDIR=$(mktemp -d)
74-
mkdir -p "$WORKDIR/bin" "$WORKDIR/tmp"
75-
ln -s "$CF_PROMISES" "$WORKDIR/bin/cf-promises"
76-
77-
# Run a copy of the policy, keeping its path relative to lib.cf, as
78-
# CFEngine refuses to read policy files writable by group or others, which
79-
# those of a checkout made with a umask of 002 are
80-
rel=${t#"$PWD"/}
81-
policy="$WORKDIR/policy/${rel%.sh}.cf"
82-
mkdir -p "$(dirname "$policy")"
83-
cp lib.cf "$WORKDIR/policy/"
84-
cp "${t%.sh}.cf" "$policy"
85-
chmod 600 "$WORKDIR/policy/lib.cf" "$policy"
86-
87-
CFENGINE_TEST_OVERRIDE_WORKDIR="$WORKDIR"
88-
LOG="$WORKDIR/reactor.log"
89-
PROMISE_NAME=$(basename "$t" .sh)
90-
export WORKDIR CFENGINE_TEST_OVERRIDE_WORKDIR LOG PROMISE_NAME
91-
92-
sh "$t" "$policy" || error "$t"
93-
94-
rm -rf "$WORKDIR"
67+
run_test "$t"
9568
done
9669

97-
exit $fail
70+
echo ""
71+
echo "=============================="
72+
echo "Test Results: $passed passed, $failed failed, $skipped skipped (total: $total, $(($(now) - suite_start))s)"
73+
if [ "$failed" -gt 0 ]; then
74+
echo ""
75+
echo "Failed tests:"
76+
printf '%s' "$failures"
77+
exit 1
78+
fi
79+
echo "=============================="
80+
echo "All reactor tests passed"

0 commit comments

Comments
 (0)