blob: bc026d47f305eb551f3381bd0eb82708cae1b2b5 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
#!/usr/bin/env bash
#
# Bidirectional traceability check (docs/process.md §5).
#
# Verifies HLR -> LLR -> source -> test in both directions:
# - every low-level requirement is implemented and tested
# - every requirement a test names actually exists
#
# The third check is the one that is easy to omit and that fails silently when
# omitted: it catches stale references left behind by a renumbered or withdrawn
# requirement.
#
# Exits non-zero if any check fails, so it can gate a commit or CI job.
set -euo pipefail
cd "$(dirname "$0")/.."
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
grep -ohE 'MJB-LLR-[0-9]+' docs/requirements/llr.md | sort -u > "$tmp/defined"
grep -rohE 'MJB-LLR-[0-9]+' src/ | sort -u > "$tmp/tagged"
grep -rohE 'mjb_llr_[0-9]+' src/ tests/ \
| sed 's/mjb_llr_/MJB-LLR-/' | sort -u > "$tmp/tested"
defined=$(wc -l < "$tmp/defined")
tagged=$(wc -l < "$tmp/tagged")
tested=$(wc -l < "$tmp/tested")
printf 'defined=%s tagged=%s tested=%s\n\n' "$defined" "$tagged" "$tested"
status=0
report() { # $1=label $2=file $3=explanation
if [ -s "$2" ]; then
printf '%s:\n' "$1"
sed 's/^/ /' "$2"
printf ' -> %s\n\n' "$3"
status=1
fi
}
comm -23 "$tmp/defined" "$tmp/tagged" > "$tmp/untagged"
comm -23 "$tmp/defined" "$tmp/tested" > "$tmp/untested"
comm -13 "$tmp/defined" "$tmp/tested" > "$tmp/unknown"
report "Requirements with no implementation" "$tmp/untagged" \
"tag the implementing item with // MJB-LLR-nnn"
report "Requirements with no test" "$tmp/untested" \
"add a test named mjb_llr_nnn_<description>"
report "Tests naming a requirement that does not exist" "$tmp/unknown" \
"typo, or a stale reference to a withdrawn requirement"
if [ "$status" -eq 0 ]; then
echo "OK: traceability complete in both directions."
fi
exit "$status"
|