Fix: Close verification gate review findings
This commit is contained in:
+184
-24
@@ -89,6 +89,8 @@ ${BOLD}Commands:${RESET}
|
||||
pattern to run a subset. --safe selects hermetic contracts only.
|
||||
Plain terminal runs prompt before non-hermetic work. Automation
|
||||
must grant each required capability with a repeatable --allow.
|
||||
Each non-hermetic contract announces its exact capabilities
|
||||
before it starts.
|
||||
Failures print captured stdout/stderr. Successful stdout stays
|
||||
quiet; successful stderr is a warning. The default outer timeout
|
||||
is 180 seconds. Set PANAMA_TEST_TIMEOUT_SECONDS to a positive
|
||||
@@ -447,12 +449,20 @@ PROMPT
|
||||
CONTRACT_MANIFEST="tests/contracts.manifest"
|
||||
CONTRACT_CAPABILITIES=(hermetic live-host live-compositor live-desktop network privileged)
|
||||
|
||||
contract_paths() {
|
||||
local candidate
|
||||
while IFS= read -r candidate; do
|
||||
[[ -x "$candidate" || "$candidate" == *_test.py ]] || continue
|
||||
printf 'tests/%s\n' "${candidate#"$PANAMA_DIR/tests/"}"
|
||||
done < <(find "$PANAMA_DIR/tests" -type f \
|
||||
-not -path '*/fixtures/*' -not -path '*__pycache__*' | sort)
|
||||
}
|
||||
|
||||
contract_manifest_entries() {
|
||||
local line capabilities path
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line%%#*}"
|
||||
read -r capabilities path _ <<<"$line"
|
||||
[[ -n "${capabilities:-}" && -n "${path:-}" ]] || continue
|
||||
[[ "$line" =~ ^[[:space:]]*(#|$) ]] && continue
|
||||
IFS=$' \t' read -r capabilities path <<<"$line"
|
||||
printf '%s\t%s\n' "$path" "$capabilities"
|
||||
done < "$PANAMA_DIR/$CONTRACT_MANIFEST"
|
||||
}
|
||||
@@ -464,6 +474,98 @@ require_contract_manifest() {
|
||||
}
|
||||
}
|
||||
|
||||
validate_contract_manifest() {
|
||||
require_contract_manifest || return 1
|
||||
|
||||
local manifest="$PANAMA_DIR/$CONTRACT_MANIFEST"
|
||||
local line capabilities path extra previous_comment="" previous_was_comment=0
|
||||
local previous_path="" capability discovered
|
||||
local -a capability_list=() findings=()
|
||||
local -A expected_contracts=() manifest_paths=()
|
||||
|
||||
while IFS= read -r discovered; do
|
||||
expected_contracts["$discovered"]=1
|
||||
done < <(contract_paths)
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ "$line" =~ ^[[:space:]]*# ]]; then
|
||||
previous_comment="${line#*#}"
|
||||
previous_comment="${previous_comment#"${previous_comment%%[![:space:]]*}"}"
|
||||
previous_comment="${previous_comment%"${previous_comment##*[![:space:]]}"}"
|
||||
previous_was_comment=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$line" =~ ^[[:space:]]*$ ]]; then
|
||||
previous_comment=""
|
||||
previous_was_comment=0
|
||||
continue
|
||||
fi
|
||||
|
||||
IFS=$' \t' read -r capabilities path extra <<<"$line"
|
||||
if [[ -z "${capabilities:-}" || -z "${path:-}" || -n "${extra:-}" ]]; then
|
||||
findings+=("manifest line is not exactly two fields: $line")
|
||||
previous_comment=""
|
||||
previous_was_comment=0
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ -n "$previous_path" && "$path" < "$previous_path" ]]; then
|
||||
findings+=('paths are not lexicographically sorted')
|
||||
fi
|
||||
previous_path="$path"
|
||||
|
||||
if [[ -n "${manifest_paths[$path]:-}" ]]; then
|
||||
findings+=("duplicate path $path")
|
||||
fi
|
||||
manifest_paths["$path"]=1
|
||||
|
||||
local -A line_capabilities=()
|
||||
if [[ "$capabilities" == ,* || "$capabilities" == *, || "$capabilities" == *,,* ]]; then
|
||||
findings+=("empty capability on $path")
|
||||
fi
|
||||
IFS=',' read -r -a capability_list <<<"$capabilities"
|
||||
for capability in "${capability_list[@]}"; do
|
||||
[[ -n "$capability" ]] || continue
|
||||
if [[ -n "${line_capabilities[$capability]:-}" ]]; then
|
||||
findings+=("duplicate capability $capability on $path")
|
||||
fi
|
||||
line_capabilities["$capability"]=1
|
||||
is_contract_capability "$capability" \
|
||||
|| findings+=("unknown capability $capability on $path")
|
||||
done
|
||||
|
||||
if [[ -n "${line_capabilities[hermetic]:-}" && ${#line_capabilities[@]} -ne 1 ]]; then
|
||||
findings+=("hermetic must appear alone on $path")
|
||||
fi
|
||||
|
||||
if [[ "$capabilities" != hermetic ]]; then
|
||||
if (( previous_was_comment != 1 )); then
|
||||
findings+=("$path is non-hermetic but lacks a directly preceding comment")
|
||||
elif [[ -z "$previous_comment" ]]; then
|
||||
findings+=("$path is non-hermetic but lacks a non-empty directly preceding comment")
|
||||
fi
|
||||
fi
|
||||
previous_comment=""
|
||||
previous_was_comment=0
|
||||
done < "$manifest"
|
||||
|
||||
for discovered in "${!expected_contracts[@]}"; do
|
||||
[[ -n "${manifest_paths[$discovered]:-}" ]] \
|
||||
|| findings+=("missing contract $discovered")
|
||||
done
|
||||
for path in "${!manifest_paths[@]}"; do
|
||||
[[ -n "${expected_contracts[$path]:-}" ]] \
|
||||
|| findings+=("stale manifest path $path")
|
||||
done
|
||||
|
||||
if (( ${#findings[@]} > 0 )); then
|
||||
err "Contract manifest validation failed with ${#findings[@]} finding(s):"
|
||||
printf ' - %s\n' "${findings[@]}" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_usage() {
|
||||
err "Usage: ${BOLD}$PROGRAM test [--safe] [--allow <capability>] [pattern]${RESET}"
|
||||
return 2
|
||||
@@ -492,10 +594,59 @@ is_contract_capability() {
|
||||
# --safe runs only contracts the manifest classifies as hermetic and reports
|
||||
# each external capability it skipped. A plain terminal run asks before any
|
||||
# selected non-hermetic work. Automation must grant every required capability
|
||||
# with repeatable --allow flags. Each contract gets an outer timeout, 180
|
||||
# with repeatable --allow flags. Non-hermetic contracts announce their exact
|
||||
# capability list before execution. Each contract gets an outer timeout, 180
|
||||
# seconds by default. PANAMA_TEST_TIMEOUT_SECONDS accepts a positive integer
|
||||
# override. Failures include captured stdout and stderr. Successful stdout stays
|
||||
# quiet, while successful stderr is surfaced as a warning.
|
||||
# override. Failures include captured stdout and stderr. Successful stdout
|
||||
# stays quiet, while successful stderr is surfaced as a warning.
|
||||
PANAMA_ACTIVE_CONTRACT_PID=""
|
||||
PANAMA_CONTRACT_CAPTURE_DIR=""
|
||||
|
||||
cleanup_contract_capture() {
|
||||
if [[ -n "$PANAMA_CONTRACT_CAPTURE_DIR" && -d "$PANAMA_CONTRACT_CAPTURE_DIR" ]]; then
|
||||
rm -rf -- "$PANAMA_CONTRACT_CAPTURE_DIR" || true
|
||||
fi
|
||||
PANAMA_CONTRACT_CAPTURE_DIR=""
|
||||
}
|
||||
|
||||
terminate_active_contract() {
|
||||
local pid="$PANAMA_ACTIVE_CONTRACT_PID"
|
||||
PANAMA_ACTIVE_CONTRACT_PID=""
|
||||
[[ "$pid" =~ ^[1-9][0-9]*$ && "$pid" != "$$" ]] || return 0
|
||||
|
||||
# GNU timeout owns a process group whose ID is its PID. Signal that complete
|
||||
# group so a contract cannot leave descendants behind, with a direct-PID
|
||||
# fallback for implementations that do not create the group.
|
||||
kill -TERM -- "-$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true
|
||||
wait "$pid" 2>/dev/null || true
|
||||
}
|
||||
|
||||
handle_contract_signal() {
|
||||
local signal_status="$1"
|
||||
trap - INT TERM
|
||||
terminate_active_contract
|
||||
cleanup_contract_capture
|
||||
trap - EXIT
|
||||
exit "$signal_status"
|
||||
}
|
||||
|
||||
prepare_contract_capture() {
|
||||
local capture_dir=""
|
||||
if ! capture_dir="$(mktemp -d)"; then
|
||||
err 'Could not create contract capture directory.'
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$capture_dir" || ! -d "$capture_dir" ]]; then
|
||||
err 'Could not create contract capture directory.'
|
||||
return 1
|
||||
fi
|
||||
|
||||
PANAMA_CONTRACT_CAPTURE_DIR="$capture_dir"
|
||||
trap cleanup_contract_capture EXIT
|
||||
trap 'handle_contract_signal 130' INT
|
||||
trap 'handle_contract_signal 143' TERM
|
||||
}
|
||||
|
||||
cmd_test() {
|
||||
local timeout_seconds="${PANAMA_TEST_TIMEOUT_SECONDS:-180}"
|
||||
[[ "$timeout_seconds" =~ ^[1-9][0-9]*$ ]] || {
|
||||
@@ -503,11 +654,11 @@ cmd_test() {
|
||||
return 2
|
||||
}
|
||||
|
||||
require_contract_manifest || return 1
|
||||
validate_contract_manifest || return 1
|
||||
cmd_test_impl "$timeout_seconds" "$@"
|
||||
}
|
||||
|
||||
cmd_test_impl() (
|
||||
cmd_test_impl() {
|
||||
local timeout_seconds="$1"
|
||||
shift
|
||||
local pattern="" safe=0 arg capability capabilities rel path
|
||||
@@ -605,12 +756,10 @@ cmd_test_impl() (
|
||||
fi
|
||||
fi
|
||||
|
||||
local capture_dir stdout_file stderr_file name run_status index=0
|
||||
local capture_dir stdout_file stderr_file name run_status index=0 final_status=0
|
||||
local -a failed=() runner=()
|
||||
capture_dir="$(mktemp -d)"
|
||||
trap 'rm -rf -- "$capture_dir"' EXIT
|
||||
trap 'rm -rf -- "$capture_dir"; exit 130' INT
|
||||
trap 'rm -rf -- "$capture_dir"; exit 143' TERM
|
||||
prepare_contract_capture || return 1
|
||||
capture_dir="$PANAMA_CONTRACT_CAPTURE_DIR"
|
||||
|
||||
info "Running ${#suite[@]} contract(s)"
|
||||
for index in "${!suite[@]}"; do
|
||||
@@ -624,9 +773,16 @@ cmd_test_impl() (
|
||||
else
|
||||
runner=("$path")
|
||||
fi
|
||||
capabilities="${manifest_capabilities[$rel]}"
|
||||
if [[ "$capabilities" != hermetic ]]; then
|
||||
info "Running $name [$capabilities]"
|
||||
fi
|
||||
run_status=0
|
||||
timeout --signal=TERM --kill-after=5 "$timeout_seconds" \
|
||||
"${runner[@]}" >"$stdout_file" 2>"$stderr_file" || run_status=$?
|
||||
"${runner[@]}" >"$stdout_file" 2>"$stderr_file" &
|
||||
PANAMA_ACTIVE_CONTRACT_PID=$!
|
||||
wait "$PANAMA_ACTIVE_CONTRACT_PID" || run_status=$?
|
||||
PANAMA_ACTIVE_CONTRACT_PID=""
|
||||
if (( run_status == 0 )); then
|
||||
ok "$name"
|
||||
if [[ -s "$stderr_file" ]]; then
|
||||
@@ -655,12 +811,16 @@ cmd_test_impl() (
|
||||
header "Result"
|
||||
if (( ${#failed[@]} == 0 )); then
|
||||
ok "${#suite[@]} contract(s) passed"
|
||||
return 0
|
||||
else
|
||||
err "${#failed[@]} of ${#suite[@]} failed:"
|
||||
printf ' %s\n' "${failed[@]}" >&2
|
||||
final_status=1
|
||||
fi
|
||||
err "${#failed[@]} of ${#suite[@]} failed:"
|
||||
printf ' %s\n' "${failed[@]}" >&2
|
||||
return 1
|
||||
)
|
||||
|
||||
cleanup_contract_capture
|
||||
trap - EXIT INT TERM
|
||||
return "$final_status"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Command: contracts
|
||||
@@ -721,7 +881,7 @@ cmd_contracts() {
|
||||
suffix="${suffix#*/}"
|
||||
done
|
||||
|
||||
require_contract_manifest || return 1
|
||||
validate_contract_manifest || return 1
|
||||
local -A manifest_capabilities=()
|
||||
local capabilities
|
||||
while IFS=$'\t' read -r rel capabilities; do
|
||||
@@ -732,11 +892,11 @@ cmd_contracts() {
|
||||
# runner would actually execute.
|
||||
local -a hits=()
|
||||
local candidate rel
|
||||
while IFS= read -r candidate; do
|
||||
[[ -x "$candidate" || "$candidate" == *_test.py ]] || continue
|
||||
while IFS= read -r rel; do
|
||||
candidate="$PANAMA_DIR/$rel"
|
||||
grep -qF "${patterns[@]}" "$candidate" 2>/dev/null || continue
|
||||
hits+=("tests/${candidate#"$PANAMA_DIR"/tests/}")
|
||||
done < <(find "$PANAMA_DIR/tests" -type f -not -path '*/fixtures/*' -not -path '*__pycache__*' | sort)
|
||||
hits+=("$rel")
|
||||
done < <(contract_paths)
|
||||
|
||||
if (( ${#hits[@]} == 0 )); then
|
||||
printf 'No contract mentions %s — coverage may be indirect (a harness or a generated artifact); nothing verified.\n' "$path" >&2
|
||||
|
||||
Reference in New Issue
Block a user