|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Test can_diag.sh error frame classification (Issue #9 review C1-C4): |
| 4 | +# - the candump -L line regex actually matches and parses (C1) |
| 5 | +# - the format matches real `candump -L -e any,0~0,#FFFFFFFF` output (C2) |
| 6 | +# - classification follows linux/can/error.h bit-for-bit, counting every |
| 7 | +# set bit independently rather than overwriting with "last one wins" (C3) |
| 8 | +# - the test checks exact per-category COUNTS, not just that section |
| 9 | +# headings exist (C4) |
| 10 | +# Also covers M5 (sysfs stats come from CAN_TOOLS_SYSFS_ROOT via the lib's |
| 11 | +# can_read_stats(), not a hardcoded /sys/class/net path). |
| 12 | + |
| 13 | +tmpdir=$(mktemp -d) |
| 14 | +# shellcheck disable=SC2064 |
| 15 | +trap "rm -rf '$tmpdir'" EXIT |
| 16 | + |
| 17 | +pass=0 fail=0 |
| 18 | + |
| 19 | +check() { |
| 20 | + if [[ "$1" == "0" ]]; then |
| 21 | + pass=$((pass+1)) |
| 22 | + else |
| 23 | + fail=$((fail+1)) |
| 24 | + echo "FAIL: $2" |
| 25 | + fi |
| 26 | +} |
| 27 | + |
| 28 | +export CAN_TOOLS_RUN_DIR="$tmpdir/run" |
| 29 | +export CAN_TOOLS_FAKE_DIR="$tmpdir/fake" |
| 30 | + |
| 31 | +fakes_path=$(cd "$(dirname "$0")/fakes" && pwd) || exit 1 |
| 32 | +export CAN_TOOLS_IP_CMD="$fakes_path/ip" |
| 33 | +export CAN_TOOLS_JOURNALCTL_CMD="$fakes_path/journalctl" |
| 34 | + |
| 35 | +diag_fakes_path=$(cd "$(dirname "$0")/diag_fakes" && pwd) || exit 1 |
| 36 | +export CAN_TOOLS_CANDUMP_CMD="$diag_fakes_path/candump" |
| 37 | + |
| 38 | +mkdir -p "$tmpdir/run" "$tmpdir/fake" "$tmpdir/fake/can0" "$tmpdir/fake/can1" |
| 39 | + |
| 40 | +echo "ERROR-ACTIVE" >"$tmpdir/fake/can0/state" |
| 41 | +echo "restart_ms=100 berr=off fd=off admin=up" >"$tmpdir/fake/can0/config" |
| 42 | +echo "ERROR-ACTIVE" >"$tmpdir/fake/can1/state" |
| 43 | +echo "restart_ms=100 berr=off fd=off admin=up" >"$tmpdir/fake/can1/config" |
| 44 | + |
| 45 | +mkdir -p "$tmpdir/sys/class/net/can0/statistics" |
| 46 | +mkdir -p "$tmpdir/sys/class/net/can1/statistics" |
| 47 | +echo "123" >"$tmpdir/sys/class/net/can0/statistics/rx_packets" |
| 48 | +echo "50" >"$tmpdir/sys/class/net/can0/statistics/tx_packets" |
| 49 | +echo "7" >"$tmpdir/sys/class/net/can0/statistics/rx_fifo_errors" |
| 50 | +echo "0" >"$tmpdir/sys/class/net/can0/statistics/tx_fifo_errors" |
| 51 | +echo "1" >"$tmpdir/sys/class/net/can0/ifindex" |
| 52 | +echo "100" >"$tmpdir/sys/class/net/can1/statistics/rx_packets" |
| 53 | +echo "0" >"$tmpdir/sys/class/net/can1/statistics/tx_packets" |
| 54 | +echo "0" >"$tmpdir/sys/class/net/can1/statistics/rx_fifo_errors" |
| 55 | +echo "0" >"$tmpdir/sys/class/net/can1/statistics/tx_fifo_errors" |
| 56 | + |
| 57 | +export CAN_TOOLS_SYSFS_ROOT="$tmpdir/sys" |
| 58 | +export CAN_TOOLS_FAKE_TIME_FILE="$tmpdir/time" |
| 59 | +echo "1700000000" >"$CAN_TOOLS_FAKE_TIME_FILE" |
| 60 | + |
| 61 | +echo "Testing can_diag.sh classification logic..." |
| 62 | + |
| 63 | +cd "$(dirname "$0")/.." || exit 1 |
| 64 | + |
| 65 | +output=$(bash can_diag.sh 3 2>&1) |
| 66 | +rc=$? |
| 67 | +check "$rc" "can_diag.sh should exit 0 (got rc=$rc); output follows:\n$output" |
| 68 | + |
| 69 | +diag_file=$(echo "$output" | grep "Diagnostics saved" | awk '{print $NF}') |
| 70 | +if [[ -n "$diag_file" && -f "$diag_file" ]]; then |
| 71 | + check 0 "can_diag.sh should create output file" |
| 72 | +else |
| 73 | + check 1 "can_diag.sh should create output file" |
| 74 | +fi |
| 75 | + |
| 76 | +if [[ -f "$diag_file" ]]; then |
| 77 | + echo "DEBUG: Full diag file:" >&2 |
| 78 | + cat "$diag_file" >&2 |
| 79 | + |
| 80 | + for section in "IP Link Show" "sysfs Statistics" "kernel log" "CAN Error Frames" "Error Frame Summary" "Interface Setup"; do |
| 81 | + grep -q "$section" "$diag_file" |
| 82 | + check $? "Output should contain '$section' section" |
| 83 | + done |
| 84 | + |
| 85 | + # --- C1/C2: the regex actually matched real -L lines; raw matched error |
| 86 | + # frame lines (not the one ordinary data frame) must be echoed verbatim. |
| 87 | + grep -q '^(1697000000\.000002) can0 20000020#0000000000000000$' "$diag_file" |
| 88 | + check $? "raw -L error-frame line should be preserved verbatim (C1/C2)" |
| 89 | + |
| 90 | + # the ordinary (non-error) data frame must not be written to the diag |
| 91 | + # file at all: diag_classify_line() rejects it (no CAN_ERR_FLAG) before |
| 92 | + # it would ever be echoed. |
| 93 | + if grep -qF '18DA31F9' "$diag_file"; then |
| 94 | + check 1 "ordinary (non-error) frame must not appear in the diag file" |
| 95 | + else |
| 96 | + check 0 "ordinary (non-error) frame must not appear in the diag file" |
| 97 | + fi |
| 98 | + |
| 99 | + # --- C3/C4: exact expected counts per category, from the fake's own |
| 100 | + # documented scenario (tests/diag_fakes/candump). |
| 101 | + declare -A expected=( |
| 102 | + ["can0 class=ACK count=1"]=1 |
| 103 | + ["can0 class=BUSOFF count=1"]=1 |
| 104 | + ["can0 class=PROT count=2"]=1 |
| 105 | + ["can0 class=BUSERROR count=1"]=1 |
| 106 | + ["can0 class=CRTL count=1"]=1 |
| 107 | + ["can0 prot=TX count=1"]=1 |
| 108 | + ["can0 loc=ACK count=1"]=1 |
| 109 | + ["can0 prot=FORM count=1"]=1 |
| 110 | + ["can0 ctrl=RX_WARNING count=1"]=1 |
| 111 | + ["can1 class=TRX count=1"]=1 |
| 112 | + ["can1 trx=CANH_SHORT_TO_GND count=1"]=1 |
| 113 | + ) |
| 114 | + for want in "${!expected[@]}"; do |
| 115 | + grep -qF "$want" "$diag_file" |
| 116 | + check $? "Error Frame Summary should contain exact count: '$want'" |
| 117 | + done |
| 118 | + |
| 119 | + # A key that must NOT appear (sanity: no spurious class=ACK on can1, no |
| 120 | + # bit0 misfire on the FORM-only frame). |
| 121 | + if grep -qF "can1 class=ACK" "$diag_file"; then |
| 122 | + check 1 "must not classify can1 as ACK (no such frame in the scenario)" |
| 123 | + else |
| 124 | + check 0 "must not classify can1 as ACK (no such frame in the scenario)" |
| 125 | + fi |
| 126 | + |
| 127 | + # --- M5: sysfs values came from CAN_TOOLS_SYSFS_ROOT (can_read_stats), |
| 128 | + # not a hardcoded /sys/class/net path - check the actual fixture values. |
| 129 | + grep -q "rx_packets: 123" "$diag_file" |
| 130 | + check $? "sysfs rx_packets should reflect CAN_TOOLS_SYSFS_ROOT fixture (M5)" |
| 131 | + grep -q "rx_fifo_errors: 7" "$diag_file" |
| 132 | + check $? "sysfs rx_fifo_errors should reflect CAN_TOOLS_SYSFS_ROOT fixture (M5)" |
| 133 | +fi |
| 134 | + |
| 135 | +# berr-reporting restored to original (off) after a clean run (H1) |
| 136 | +final_berr=$(grep -o "berr=[^ ]*" "$tmpdir/fake/can0/config") |
| 137 | +[[ "$final_berr" == "berr=off" ]] |
| 138 | +check $? "berr-reporting should be restored to off (got: $final_berr)" |
| 139 | + |
| 140 | +[[ ! -f "$tmpdir/run/watchdog.pause" ]] |
| 141 | +check $? "watchdog.pause should be cleaned up" |
| 142 | + |
| 143 | +[[ ! -f "$tmpdir/run/lock" || ! -f "/proc/$$/fd/9" ]] |
| 144 | +check $? "Lock should be released" |
| 145 | + |
| 146 | +echo "" |
| 147 | +echo "Results: $pass passed, $fail failed" |
| 148 | + |
| 149 | +if (( fail > 0 )); then |
| 150 | + exit 1 |
| 151 | +fi |
| 152 | + |
| 153 | +exit 0 |
0 commit comments