mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix] (Test): stop G7's descriptor control carrying on past a Ctrl-C - latch the interrupt from the trap and from a signalled child, and report it as an interruption rather than as the contract tree's verdict
This commit is contained in:
@@ -88,7 +88,11 @@
|
|||||||
# very build directory, and every reading they take there would come from a deliberately corrupted
|
# very build directory, and every reading they take there would come from a deliberately corrupted
|
||||||
# library. So repair() runs from the EXIT trap as well - a mid-way failure (a patched header that
|
# library. So repair() runs from the EXIT trap as well - a mid-way failure (a patched header that
|
||||||
# would not compile, an interrupt) repairs too - and a repair that itself fails downgrades the
|
# would not compile, an interrupt) repairs too - and a repair that itself fails downgrades the
|
||||||
# verdict to 2. On the exits that happen BEFORE any patch (bad arguments, a missing header, a
|
# verdict to 2. The state repair() reads lives in THIS shell and not in a command substitution,
|
||||||
|
# which is what makes the trap version of it a real repair rather than a no-op, and every child's
|
||||||
|
# exit status is additionally checked for "killed by a signal" so that a Ctrl-C stops the run
|
||||||
|
# instead of carrying on into the next control and signing off with a verdict about a control
|
||||||
|
# nobody ran. On the exits that happen BEFORE any patch (bad arguments, a missing header, a
|
||||||
# missing suite) the tree was never touched and the build directory still holds what the caller
|
# missing suite) the tree was never touched and the build directory still holds what the caller
|
||||||
# built: there is nothing to restore and nothing to rebuild, and the script says which of the two
|
# built: there is nothing to restore and nothing to rebuild, and the script says which of the two
|
||||||
# situations it is leaving behind.
|
# situations it is leaving behind.
|
||||||
@@ -102,10 +106,11 @@
|
|||||||
# red without ever naming the field, so the red cannot be attributed to the drop.
|
# red without ever naming the field, so the red cannot be attributed to the drop.
|
||||||
# Both are findings about the TEST, not errors in this script - and both leave the
|
# Both are findings about the TEST, not errors in this script - and both leave the
|
||||||
# tree restored AND rebuilt AND re-run;
|
# tree restored AND rebuilt AND re-run;
|
||||||
# 2 a control could not be run at all (bad arguments; a header or a field absent on
|
# 2 a control could not be run at all (bad arguments; a build directory that is not a
|
||||||
# this tree; no matching test; a build that was already broken; a patched header that
|
# push one; a header or a field absent on this tree; no matching test; a build that
|
||||||
# did not compile; a failed restore or a failed rebuild after one). Exit 2 wins over
|
# was already broken; a patched header that did not compile; a failed restore or a
|
||||||
# exit 1: "could not run" is never reported as "did not answer".
|
# failed rebuild after one; the run was INTERRUPTED). Exit 2 wins over exit 1:
|
||||||
|
# "could not run" is never reported as "did not answer".
|
||||||
set -u -o pipefail
|
set -u -o pipefail
|
||||||
|
|
||||||
BUILD_DIR=""
|
BUILD_DIR=""
|
||||||
@@ -203,9 +208,23 @@ repair() {
|
|||||||
# that reader does is build, from a hard-wired field, with nothing saying so. The two extra traps
|
# that reader does is build, from a hard-wired field, with nothing saying so. The two extra traps
|
||||||
# repair and then re-raise with the default disposition, so the exit status still reports the
|
# repair and then re-raise with the default disposition, so the exit status still reports the
|
||||||
# signal. Armed for the whole run: before the first patch repair() is a no-op.
|
# signal. Armed for the whole run: before the first patch repair() is a no-op.
|
||||||
|
#
|
||||||
|
# AND THE TRAPS ARE NOT THE ONLY DETECTION, because bash's SIGINT semantics do not guarantee that
|
||||||
|
# they run. A Ctrl-C in a terminal goes to the whole PROCESS GROUP, so the `cmake` this script is
|
||||||
|
# waiting on dies first and returns 130 - and bash, having a handler installed, may go on to the
|
||||||
|
# next command rather than to the trap. Measured exactly that way: the tree WAS repaired (through
|
||||||
|
# the explicit failure path below, which is why the state has to live in this shell), but the run
|
||||||
|
# then carried on into the second control and signed off with the summary it prints on an
|
||||||
|
# untouched contract tree. Repaired and wrong is still wrong. So every child's status is checked
|
||||||
|
# for "killed by a signal" as well, INTERRUPTED latches either way, and the run stops and says so.
|
||||||
|
INTERRUPTED=""
|
||||||
|
note_interrupt() { [ -n "$INTERRUPTED" ] || INTERRUPTED=$1; }
|
||||||
|
# 128 + signal number is how a shell reports a child that died on a signal; nothing this script
|
||||||
|
# runs exits above 128 for any other reason (ctest uses 8 for failing tests).
|
||||||
|
child_was_signalled() { [ "${1:-0}" -ge 128 ]; }
|
||||||
|
trap 'note_interrupt INT; repair; trap - INT; kill -INT $$' INT
|
||||||
|
trap 'note_interrupt TERM; repair; trap - TERM; kill -TERM $$' TERM
|
||||||
trap 'repair' EXIT
|
trap 'repair' EXIT
|
||||||
trap 'repair; trap - INT; kill -INT $$' INT
|
|
||||||
trap 'repair; trap - TERM; kill -TERM $$' TERM
|
|
||||||
|
|
||||||
# --- one control -----------------------------------------------------------------------------
|
# --- one control -----------------------------------------------------------------------------
|
||||||
# Sets the GLOBAL CONTROL_VERDICT to "tripped" / "did-not-trip" / "wrong-reason" /
|
# Sets the GLOBAL CONTROL_VERDICT to "tripped" / "did-not-trip" / "wrong-reason" /
|
||||||
@@ -329,7 +348,17 @@ PY
|
|||||||
# 3. it must still COMPILE. A build break here would prove the static_asserts work, not that the
|
# 3. it must still COMPILE. A build break here would prove the static_asserts work, not that the
|
||||||
# suite still checks.
|
# suite still checks.
|
||||||
say "[$tag] rebuilding with the dropped field"
|
say "[$tag] rebuilding with the dropped field"
|
||||||
if ! cmake --build "$BUILD_DIR" -j "$(nproc)" </dev/null > "$LOG_DIR/build-after-$tag.log" 2>&1; then
|
cmake --build "$BUILD_DIR" -j "$(nproc)" </dev/null > "$LOG_DIR/build-after-$tag.log" 2>&1
|
||||||
|
buildRc=$?
|
||||||
|
if child_was_signalled "$buildRc" || [ -n "$INTERRUPTED" ]; then
|
||||||
|
note_interrupt "exit $buildRc"
|
||||||
|
say "[$tag] INTERRUPTED during the rebuild ($INTERRUPTED). Repairing the tree and stopping:"
|
||||||
|
say "a run that carried on here would report a verdict about a control that never ran."
|
||||||
|
repair || true
|
||||||
|
CONTROL_VERDICT=could-not-run
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ "$buildRc" -ne 0 ]; then
|
||||||
say "[$tag] the patched header did not compile, so the control cannot tell 'the test failed'"
|
say "[$tag] the patched header did not compile, so the control cannot tell 'the test failed'"
|
||||||
say "from 'nothing was built'. The break is supposed to be invisible to the compiler - if the"
|
say "from 'nothing was built'. The break is supposed to be invisible to the compiler - if the"
|
||||||
say "field is read somewhere that needs its value, say so in the control rather than working"
|
say "field is read somewhere that needs its value, say so in the control rather than working"
|
||||||
@@ -345,8 +374,18 @@ PY
|
|||||||
# reported and nothing returns until step 5 has put the tree back, because all three outcomes
|
# reported and nothing returns until step 5 has put the tree back, because all three outcomes
|
||||||
# leave the same corrupted build directory behind.
|
# leave the same corrupted build directory behind.
|
||||||
say "[$tag] running $test against the dropped field"
|
say "[$tag] running $test against the dropped field"
|
||||||
if ctest --test-dir "$BUILD_DIR" -R "$test" --no-tests=error --output-on-failure </dev/null \
|
ctest --test-dir "$BUILD_DIR" -R "$test" --no-tests=error --output-on-failure </dev/null \
|
||||||
> "$LOG_DIR/ctest-after-$tag.log" 2>&1; then
|
> "$LOG_DIR/ctest-after-$tag.log" 2>&1
|
||||||
|
ctestRc=$?
|
||||||
|
if child_was_signalled "$ctestRc" || [ -n "$INTERRUPTED" ]; then
|
||||||
|
note_interrupt "exit $ctestRc"
|
||||||
|
say "[$tag] INTERRUPTED while running $test ($INTERRUPTED). A suite that was killed is not a"
|
||||||
|
say "suite that went red: repairing the tree and stopping rather than scoring the control."
|
||||||
|
repair || true
|
||||||
|
CONTROL_VERDICT=could-not-run
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ "$ctestRc" -eq 0 ]; then
|
||||||
CONTROL_VERDICT=did-not-trip
|
CONTROL_VERDICT=did-not-trip
|
||||||
elif awk '/: Failure$/ || /: error:/ { block = 1 } block { print } /^[[:space:]]*$/ { block = 0 }' \
|
elif awk '/: Failure$/ || /: error:/ { block = 1 } block { print } /^[[:space:]]*$/ { block = 0 }' \
|
||||||
"$LOG_DIR/ctest-after-$tag.log" | grep -q "$field"; then
|
"$LOG_DIR/ctest-after-$tag.log" | grep -q "$field"; then
|
||||||
@@ -422,6 +461,12 @@ for mglRow in "${CONTROL_ROWS[@]}"; do
|
|||||||
could-not-run) WORST=2 ;;
|
could-not-run) WORST=2 ;;
|
||||||
*) [ "$WORST" -eq 2 ] || WORST=1 ;;
|
*) [ "$WORST" -eq 2 ] || WORST=1 ;;
|
||||||
esac
|
esac
|
||||||
|
if [ -n "$INTERRUPTED" ]; then
|
||||||
|
SUMMARY="$SUMMARY
|
||||||
|
(INTERRUPTED - the remaining control(s) did not run)"
|
||||||
|
WORST=2
|
||||||
|
break
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
trap - EXIT
|
trap - EXIT
|
||||||
@@ -429,7 +474,14 @@ say "---- G7 (P4a descriptor emission) ----$SUMMARY"
|
|||||||
case "$WORST" in
|
case "$WORST" in
|
||||||
0) say "both controls tripped and named their field; exit 0" ;;
|
0) say "both controls tripped and named their field; exit 0" ;;
|
||||||
1) say "a control did not answer; exit 1" ;;
|
1) say "a control did not answer; exit 1" ;;
|
||||||
2) say "a control could not be run; exit 2 (this is the expected answer on the P4a contract tree,"
|
2) if [ -n "$INTERRUPTED" ]; then
|
||||||
say "where both emit headers are the contract's stubs and copy nothing)" ;;
|
say "the run was INTERRUPTED ($INTERRUPTED); exit 2. The tree was repaired before this line -"
|
||||||
|
say "\`git status\` is the check - and NOTHING here is a verdict about the controls."
|
||||||
|
else
|
||||||
|
say "a control could not be run; exit 2 (this is the expected answer on the P4a contract tree,"
|
||||||
|
say "where both emit headers are the contract's stubs and copy nothing)"
|
||||||
|
fi ;;
|
||||||
esac
|
esac
|
||||||
|
# A run this shell interrupted still exits 2 rather than by signal, because the INT trap's
|
||||||
|
# re-raise only happens when bash actually reaches the trap. The summary above says which it was.
|
||||||
exit "$WORST"
|
exit "$WORST"
|
||||||
|
|||||||
Reference in New Issue
Block a user