# BST-TIMEOUT: 300 # MEASURED: see the ==BST-SUITE line of a run. ~40 cases, # # every one of them a short-lived fork of a 2-3 MB static # # binary; the suite has no daemon, no socket and no sleep. # # 300 is the ticket-gate/nightly default and leaves the # # usual multiple of headroom over the measured figure. # # can-utils 2023.03 (SRCREV cfe41963, meta-oe recipes-extended/socketcan) -- the # linux-can project's 31 command-line tools for SocketCAN. # # --------------------------------------------------------------------------- # THIS IMAGE HAS NO CAN, AND THAT DECIDES THE WHOLE SHAPE OF THIS FILE. # # arch/wasm32/configs/wasm32_defconfig sets CONFIG_NET=y and carries no # CONFIG_CAN line at all, so the CAN protocol family is not registered and # socket(PF_CAN, SOCK_RAW, CAN_RAW) answers EAFNOSUPPORT. There is no can0, and # no vcan either -- vcan is a CAN driver, so it does not exist to be created. # # A suite that only drove candump and cansend would therefore report the same # thing for a healthy port and for a package miscompiled into rubble: "no". So # this file scores three groups apart, and says which is which: # # OFFLINE -- the tools that read and write FILES and never open a socket. # can-calc-bit-timing is pure arithmetic; log2asc, asc2log and # log2long are format converters; canplayer's `stdout=` hook # prints logfile lines instead of sending them. These are asserted # FOR REAL, against values measured from an x86-64 build of the # same commit with the same patches. They are the proof that the # port produces correct code, and they are most of this file. # # PRE-BUS -- the argument validation and usage text of the bus tools, which # upstream performs BEFORE the socket call. `cansend ZZZ` # reaches "Wrong CAN-frame format!" without a socket, because # parse_canframe() runs first. These are asserted for real too. # # BUS -- everything that binds AF_CAN. PROBED at run time, and skipped # only on a probe that confirms no CAN interface exists. The probe # is cansend's own operation, in two stages, because "no family" # and "no interface" are different answers and only the second is # what upstream's own error path is about: # `socket: ...` the family is not built # `if_nametoindex: No such device` the family is there, that # interface is not # Stage one reads that class; where the family IS present, stage # two looks for an interface that resolves. Only when neither # stage finds anything to drive do these cases skip -- so on a # CAN-capable kernel they start running by themselves, with no # edit to this file, and on a kernel that has the family but no # device they skip for that reason instead of going red. # # They are not bare skips either way: each of the four is still # SCORED for running, linking, parsing its arguments and reaching # the kernel with them, which is the real statement this port can # make about those binaries here. # # ONE OF THE FOUR HAS A SECOND PROBLEM, AND IT IS NOT ABOUT CAN. # candump.c:466 calls epoll_create() before it opens its socket at # :479, and the whole epoll family is ENOSYS on wasm32 -- the # numbers are declared and classified, CONFIG_EPOLL is on and # sys_epoll_create links, but arch/wasm32/kernel/syscall_table.c is # hand-maintained and carries no epoll row, so do_syscall's NULL # check answers -ENOSYS. candump would therefore not work here even # with CONFIG_CAN turned on. That is scored as its own red, # can-utils/candump-epoll, held by brintos/linux#53 -- the suite's # only expected failure, and the only tool of the twelve installed # that has it. # # --------------------------------------------------------------------------- # THE BINARIES ARE NOT WHERE THEIR NAMES SAY. # # The recipe registers candump, cansend and cansequence through # update-alternatives, so this package installs # /usr/bin/{candump,cansend,cansequence}.can-utils and the bare names are links # the alternatives machinery owns. canutils -- a different package, same Epic -- # registers the identical three names, so which package wins the link is not # this suite's business and must not be asserted: an assertion on the target # would go red the day canutils lands, for a reason that says nothing about # can-utils. Every case below therefore drives the INSTALLED path, and the one # case that looks at the bare name checks only that the link exists. . "$BST_LIB" CU=/usr/bin CD=$CU/candump.can-utils CS=$CU/cansend.can-utils CQ=$CU/cansequence.can-utils require "$CU/can-calc-bit-timing" can-utils/present W=${TMPDIR:-/tmp}/bst-can-utils.$$ mkdir -p "$W" 2>/dev/null FX=$W/replay.log cp "$BST_DATA/can-utils-replay.log" "$FX" 2>/dev/null # --------------------------------------------------------------------------- # GROUP 1 -- the package installed what it says it installed, by real path. check can-utils/path-candump test -f "$CD" check can-utils/path-cansend test -f "$CS" check can-utils/path-cansequence test -f "$CQ" check can-utils/path-canplayer test -f "$CU/canplayer" check can-utils/path-cangen test -f "$CU/cangen" check can-utils/path-cansniffer test -f "$CU/cansniffer" check can-utils/path-log2asc test -f "$CU/log2asc" check can-utils/path-asc2log test -f "$CU/asc2log" check can-utils/path-log2long test -f "$CU/log2long" check can-utils/path-calc-bit-timing test -f "$CU/can-calc-bit-timing" check can-utils/path-canbusload test -f "$CU/canbusload" check can-utils/path-canfdtest test -f "$CU/canfdtest" # The alternatives link exists. WHERE it points is deliberately not asserted -- # canutils registers the same three names and either package may own it. check can-utils/alt-link-candump test -L "$CU/candump" check can-utils/alt-link-cansend test -L "$CU/cansend" check can-utils/alt-link-cansequence test -L "$CU/cansequence" # --------------------------------------------------------------------------- # GROUP 2 -- can-calc-bit-timing: pure arithmetic, no bus, no files. # # Every expected row below was taken from an x86-64 build of SRCREV cfe41963 # with the same two patches applied. awk's `$1=$1` re-joins the fields on single # spaces, so the assertion is on the VALUES and not on column padding, which # upstream is free to re-align. ccbt() { "$CU/can-calc-bit-timing" "$@" 2>&1; } row() { ccbt "$@" | awk 'NF>=13 { $1=$1; print; exit }'; } check_out can-utils/calc-list-flexcan "flexcan" "$CU/can-calc-bit-timing" -l check_out can-utils/calc-list-mcp251x "mcp251x" "$CU/can-calc-bit-timing" -l check_out can-utils/calc-list-at91 "at91" "$CU/can-calc-bit-timing" -l check_eq can-utils/calc-list-count 36 "$(ccbt -l | wc -l | tr -d ' ')" # flexcan at 8 MHz, 500 kbit/s -> one 32-bit CAN_CTRL register value. check_eq can-utils/calc-flexcan-500k \ "500000 125 6 7 2 1 1 500000 0.0% 87.5% 87.5% 0.0% 0x00310005" \ "$(row -q -b 500000 -c 8000000 flexcan)" # mcp251x at 8 MHz, 250 kbit/s -> the three CNF bytes, and BRP 2 rather than 1. check_eq can-utils/calc-mcp251x-250k \ "250000 250 6 7 2 1 2 250000 0.0% 87.5% 87.5% 0.0% 0x01 0xb5 0x01" \ "$(row -q -b 250000 -c 8000000 mcp251x)" # at91 at 16 MHz, 1 Mbit/s -> a controller whose CiA sample point is 75%, not # 87.5%, so a port that got the sample-point table wrong fails here and nowhere # else above. check_eq can-utils/calc-at91-1m \ "1000000 125 2 3 2 1 2 1000000 0.0% 75.0% 75.0% 0.0% 0x00010121" \ "$(row -q -b 1000000 -c 16000000 at91)" check_rc can-utils/calc-unknown-ctrl 1 "$CU/can-calc-bit-timing" -b 500000 nosuchctrl check_out can-utils/calc-unknown-msg "unknown CAN controller 'nosuchctrl'" \ sh -c "$CU/can-calc-bit-timing -b 500000 nosuchctrl 2>&1; exit 0" # --------------------------------------------------------------------------- # GROUP 3 -- log2asc / asc2log / log2long: the file converters. # # The fixture is five frames chosen to cover the format's corners: a 4-byte SFF # payload, a full 8-byte payload, a 29-bit EFF id, a remote-transmission frame # and a zero-length frame. if [ -s "$FX" ]; then bst_ok can-utils/fixture-staged else bst_notok can-utils/fixture-staged "cannot stage $BST_DATA/can-utils-replay.log"; fi check_rc can-utils/log2asc-no-interface 1 "$CU/log2asc" -I "$FX" check_out can-utils/log2asc-no-interface-msg "no CAN interfaces defined!" \ sh -c "$CU/log2asc -I '$FX' 2>&1; exit 0" "$CU/log2asc" -I "$FX" -O "$W/out.asc" vcan0 2>"$W/log2asc.err" if [ -s "$W/out.asc" ]; then bst_ok can-utils/log2asc-wrote else bst_notok can-utils/log2asc-wrote "no ASC output -- $(_bst_1line "$(cat "$W/log2asc.err" 2>/dev/null)")"; fi check_out can-utils/asc-header-base "base hex" cat "$W/out.asc" check_out can-utils/asc-header-date "date " cat "$W/out.asc" # The five converted frames, exactly. `$1=$1` again, so column padding is free. asc_row() { awk -v n="$1" 'NF>=6 && $2 ~ /^[0-9]+$/ { c++; if (c==n) { $1=$1; print; exit } }' "$W/out.asc"; } check_eq can-utils/asc-frame-sff "0.000000 1 123 Rx d 4 DE AD BE EF" "$(asc_row 1)" check_eq can-utils/asc-frame-full "0.100000 1 456 Rx d 8 11 22 33 44 55 66 77 88" "$(asc_row 2)" check_eq can-utils/asc-frame-eff "0.250000 1 1F334455x Rx d 8 00 11 22 33 44 55 66 77" "$(asc_row 3)" check_eq can-utils/asc-frame-rtr "0.500000 1 123 Rx r 0" "$(asc_row 4)" check_eq can-utils/asc-frame-zero "1.000000 1 7FF Rx d 0" "$(asc_row 5)" # asc2log back. The ASC format carries an absolute header date plus RELATIVE # offsets, and channel NUMBERS rather than interface names, so the round trip is # lossy by construction in exactly two ways: the interface comes back as the # default `can0`, and each line gains the ` R` receive-direction marker. Every # other field -- the absolute timestamp to the microsecond, the id, the payload, # the RTR and zero-length forms -- comes back identical, and that is what these # cases assert. "$CU/asc2log" -I "$W/out.asc" > "$W/back.log" 2>"$W/asc2log.err" if [ -s "$W/back.log" ]; then bst_ok can-utils/asc2log-wrote else bst_notok can-utils/asc2log-wrote "no output -- $(_bst_1line "$(cat "$W/asc2log.err" 2>/dev/null)")"; fi back_row() { awk -v n="$1" 'NR==n { $1=$1; print }' "$W/back.log"; } check_eq can-utils/rt-frame-sff "(1345212884.000000) can0 123#DEADBEEF R" "$(back_row 1)" check_eq can-utils/rt-frame-full "(1345212884.100000) can0 456#1122334455667788 R" "$(back_row 2)" check_eq can-utils/rt-frame-eff "(1345212884.250000) can0 1F334455#0011223344556677 R" "$(back_row 3)" check_eq can-utils/rt-frame-rtr "(1345212884.500000) can0 123#R R" "$(back_row 4)" check_eq can-utils/rt-frame-zero "(1345212885.000000) can0 7FF# R" "$(back_row 5)" # The absolute seconds surviving the round trip IS the 0004 patch. Unpatched, # get_date() gives up when neither en_US nor de_DE is installed -- which is this # image -- and substitutes the wall clock, so every timestamp above would carry # the time of the conversion instead of 1345212884. This case pins the fallback # by its own diagnostic, so a regression is named rather than merely observed. check_out can-utils/asc2log-c-locale-fallback "falling back to 'C'" \ sh -c "$CU/asc2log -I '$W/out.asc' 2>&1 >/dev/null; exit 0" if grep -q "Not able to determine original log file date" "$W/asc2log.err" 2>/dev/null; then bst_notok can-utils/asc2log-date-recovered "the header date was discarded -- 0004 did not take" else bst_ok can-utils/asc2log-date-recovered fi # log2long reads stdin and has no -I. A pipeline forks before the redirect, so # it is safe at the top level of a suite (see the README's redirect rule). check_eq can-utils/log2long-sff \ "(1345212884.000000) vcan0 123 [4] DE AD BE EF '....'" \ "$(cat "$FX" | "$CU/log2long" 2>/dev/null | awk 'NR==1 { $1=$1; print }')" check_eq can-utils/log2long-rtr \ "(1345212884.500000) vcan0 123 [0] remote request" \ "$(cat "$FX" | "$CU/log2long" 2>/dev/null | awk 'NR==4 { $1=$1; print }')" # --------------------------------------------------------------------------- # GROUP 4 -- canplayer's stdout hook: a replay with no bus. # # `stdout=` prints the logfile line for that interface instead of # sending it. It needs no CAN socket, and after 0003 it opens none -- which is # the only reason any of these cases can run here. Unpatched, every one of them # would report `socket: Address family not supported by protocol`, so this group # is also the port's regression guard for that patch. check_eq can-utils/canplayer-stdout-all "$(cat "$FX")" \ "$("$CU/canplayer" -I "$FX" -t stdout=vcan0 2>&1)" check_rc can-utils/canplayer-stdout-rc 0 "$CU/canplayer" -I "$FX" -t stdout=vcan0 check_eq can-utils/canplayer-loops-2 "10" \ "$("$CU/canplayer" -I "$FX" -t -l 2 stdout=vcan0 2>/dev/null | wc -l | tr -d ' ')" # -n bounds the frames SENT, not the lines printed: upstream decrements its # counter inside the sendto() branch, and the STDOUTIDX branch never reaches it. # So `-n 2` over a five-frame log still prints five. Measured, asserted as it is, # and left alone -- it is upstream's reading of "processing a CAN frame" and no # part of this port depends on the other one. check_eq can-utils/canplayer-count-is-sent-frames "5" \ "$("$CU/canplayer" -I "$FX" -t -n 2 stdout=vcan0 2>/dev/null | wc -l | tr -d ' ')" # A frame whose log-if has no assignment is skipped, not sent: get_txidx() # returns 0, and with assignments on the command line the auto-assign path does # not fire. So naming a DIFFERENT interface prints nothing and still exits 0 -- # and still opens no socket. check_eq can-utils/canplayer-unassigned "0" \ "$("$CU/canplayer" -I "$FX" -t stdout=notmine 2>/dev/null | wc -l | tr -d ' ')" # The exit status is what makes the case above discriminating: a canplayer that # died at socket() also prints nothing, so the line count alone cannot tell # "skipped every frame, cleanly" from "never started". check_rc can-utils/canplayer-unassigned-rc 0 "$CU/canplayer" -I "$FX" -t stdout=notmine # The diagnostics upstream emits before it would ever have reached a socket. # After 0003 the assignment validation genuinely runs first, so these report # what they are about rather than EAFNOSUPPORT. check_out can-utils/canplayer-missing-equals "'=' missing in assignment!" \ sh -c "$CU/canplayer -I '$FX' bogus 2>&1; exit 0" check_out can-utils/canplayer-bad-option "invalid option" \ sh -c "$CU/canplayer -Z 2>&1; exit 0" check_out can-utils/canplayer-missing-infile "infile: No such file or directory" \ sh -c "$CU/canplayer -I /nonexistent/nope.log stdout=vcan0 2>&1; exit 0" check_rc can-utils/canplayer-missing-infile-rc 1 "$CU/canplayer" -I /nonexistent/nope.log stdout=vcan0 # --------------------------------------------------------------------------- # GROUP 5 -- the bus tools' pre-socket surface: usage text and argument # validation, which upstream performs before it opens anything. # # The exit codes are upstream's and are asserted as measured, including the two # that are arguably wrong: candump and canbusload exit 0 after printing usage # for a missing argument, where cansend and cangen exit 1. check_out can-utils/cansend-usage "send CAN-frames via CAN_RAW sockets" sh -c "$CS 2>&1; exit 0" check_rc can-utils/cansend-usage-rc 1 "$CS" check_out can-utils/cansend-bad-frame "Wrong CAN-frame format!" sh -c "$CS vcan0 ZZZ 2>&1; exit 0" check_rc can-utils/cansend-bad-frame-rc 1 "$CS" vcan0 ZZZ check_out can-utils/candump-usage "dump CAN bus traffic" sh -c "$CD 2>&1; exit 0" check_rc can-utils/candump-usage-rc 0 "$CD" check_out can-utils/cangen-usage "CAN frames generator" sh -c "$CU/cangen 2>&1; exit 0" check_rc can-utils/cangen-usage-rc 1 "$CU/cangen" check_out can-utils/canbusload-usage "monitor CAN bus load" sh -c "$CU/canbusload 2>&1; exit 0" check_rc can-utils/canbusload-usage-rc 0 "$CU/canbusload" check_out can-utils/cansequence-usage "rising sequence number" sh -c "$CQ -h 2>&1; exit 0" # --------------------------------------------------------------------------- # GROUP 6 -- the runtime probe, and the bus cases it gates. # # cansend's own operation is the probe. Upstream's order in cansend.c is # parse_canframe() -> socket() -> if_nametoindex(), so the two failures are # distinguishable and say different things: # # `socket: ...` the CAN protocol family is not built # `if_nametoindex: No such device` the family is there; the interface is not # # Only the first justifies skipping. Anything else is a third answer nobody has # seen and is scored as a failure rather than quietly folded into one of the two. probe=$("$CS" vcan0 123#DEADBEEF 2>&1); probe_rc=$? case "$probe" in *"socket: "*) can_family=absent ;; *if_nametoindex*|*SIOCGIFINDEX*) can_family=present ;; *) can_family=unknown ;; esac if [ "$can_family" = unknown ]; then bst_notok can-utils/probe-af-can "unrecognised probe answer rc=$probe_rc $(_bst_1line "$probe")" else bst_ok can-utils/probe-af-can fi check_rc can-utils/probe-rc 1 "$CS" vcan0 123#DEADBEEF # Second stage: a family without an interface is still nothing to drive, and the # ticket's rule is to skip only on a probe that confirms no CAN INTERFACE # exists. So when the family is there, look for one -- anything that gets past # if_nametoindex() is a real device. can_iface= if [ "$can_family" = present ]; then for i in can0 vcan0 can1 slcan0; do o=$("$CS" "$i" 123#DEADBEEF 2>&1) case "$o" in *"No such device"*) ;; *) can_iface=$i; break ;; esac done fi # Scored in every world: each bus tool runs, links, parses its arguments and # reaches the kernel with them -- failing at socket() where the family is absent # and at the interface lookup where it is not. Both are exit 1. This is the real # statement the port can make about these four binaries here, and it is made # whether or not there is a bus. for t in "candump-reaches-kernel $CD vcan0" \ "cansend-reaches-kernel $CS vcan0 123#DEADBEEF" \ "cangen-reaches-kernel $CU/cangen -n 1 vcan0" \ "canbusload-reaches-kernel $CU/canbusload vcan0@500000"; do id=${t%% *}; cmd=${t#* } out=$(sh -c "$cmd" 2>&1 &1) case "$cd_out" in *"epoll_create"*"Function not implemented"*) bst_notok can-utils/candump-epoll "candump cannot run at all on this kernel: epoll_create() is ENOSYS (brintos/linux#53) and candump.c:466 calls it before its socket at :479 -- so this is not about CAN" ;; *) bst_ok can-utils/candump-epoll ;; esac if [ -n "$can_iface" ]; then # A CAN interface exists. Drive the four for real, bounded so nothing can # hang: candump and cangen take a frame count, canbusload a bitrate. check_rc can-utils/candump-bus 0 "$CD" -n 1 "$can_iface" check_rc can-utils/cansend-bus 0 "$CS" "$can_iface" 123#DEADBEEF check_rc can-utils/cangen-bus 0 "$CU/cangen" -n 1 "$can_iface" check_rc can-utils/canbusload-bus 0 "$CU/canbusload" "$can_iface@500000" else case "$can_family" in absent) R="no CAN protocol family on this kernel -- wasm32_defconfig carries no CONFIG_CAN, so socket(PF_CAN, ...) answers EAFNOSUPPORT and no CAN interface can exist. Same absence as 79-libsocketcan" ;; present) R="the CAN family is present but no CAN interface exists (none of can0/vcan0/can1/slcan0 resolves)" ;; *) R="the probe gave no recognised answer; not guessing which" ;; esac bst_skip can-utils/candump-bus "$R" bst_skip can-utils/cansend-bus "$R" bst_skip can-utils/cangen-bus "$R" bst_skip can-utils/canbusload-bus "$R" fi rm -rf "$W"