. "$BST_LIB" # evtest 1.36 (meta-openembedded/meta-oe recipes-test/evtest at SRCREV 96e6cb5a, # with one meta-brintos patch) -- the standard input-event debugger. One binary, # /usr/bin/evtest, out of one 1491-line evtest.c. # # ONLY THE NON-INTERACTIVE SURFACE IS MEASURED, and that is a property of the # program rather than a scoping decision. evtest has two modes and they are not # alike: # # * CAPTURE mode streams decoded events until a signal, and with no device # argument it PROMPTS -- scan_devices() prints a menu and calls scanf("%d") # on stdin. A suite that ran it plainly would either stream forever or eat # the runner's console. Every case below runs inside a command substitution # with stdin on /dev/null, so the prompt reads EOF and the menu path # terminates deterministically whether or not this image has event nodes. # * QUERY mode asks exactly one question, prints nothing on success and answers # through its EXIT CODE (0 unset, 10 set, 1 error). That is the scriptable # half, and it is what makes this package measurable here. # # FOUR FACTS ABOUT THE REAL PROGRAM SHAPE THE CASES, and three of them are not # what the summary suggests. # # 1. ARGUMENT VALIDATION HAPPENS BEFORE THE DEVICE IS OPENED. do_query() resolves # the event type, then the key name, then the bound -- and only then calls # query_device(), which is the first thing to touch open(2). So the whole # query argument surface is assertable on a machine with no input device at # all. That ordering is not assumed here: query-validation-before-open makes # it a case, by feeding an out-of-range code together with a path that cannot # be opened and requiring the bounds message rather than the open error. # # 2. `evtest:` IN AN ERROR MESSAGE IS A LITERAL, NOT argv[0]. do_capture() calls # perror("evtest") and query_device() calls perror("open") / perror("ioctl"), # all fixed strings; only version() and usage() print progname, which IS # argv[0]. The two are asserted separately -- version-line pins the argv[0] # echo by invoking the absolute path, and missing-node-perror pins the literal. # # 3. usage() WRITES TO STDOUT AND RETURNS FAILURE. Every diagnostic that precedes # it goes to stderr, so a caller reading only stdout sees the usage block with # no reason attached, and rc 1. usage-on-stdout is that case, captured without # 2>&1 so the split is measured rather than described. # # 4. AN ABSENT INPUT DEVICE IS NOT AN ERROR IN THIS PACKAGE and no case treats it # as one. What it does cost is stated plainly rather than hidden: without a # device, query_device() always ends at open(2) or at the EVIOCG* ioctl, so # the state bitmap is allocated and passed but never READ. The cases that # would read it need a synthetic device, they are named below, and they SKIP # with the probe's own measurement when uinput is unavailable. # # THE PATCH THIS PORT CARRIES IS WHY THE BOUNDS CASES ARE EXHAUSTIVE. query_device # sized its state bitmap `unsigned long state[NBITS(query_mode->max)]`, which the # HWJS-3 coroutinize pass refuses (a dynamic alloca whose address reaches ioctl); # meta-brintos sizes it from KEY_MAX instead and asserts KEY_MAX dominates the # other three maxima. The eight max/max+1 cases below are the runtime half of that # assertion: they pin every one of the four bounds the compile-time assert covers, # from outside the binary. require evtest evtest/present EV=/usr/bin/evtest NOSUCH=/bst-no-such-input-node # ev -- # stdin is /dev/null inside a substitution: the fork is what keeps a prompting # or streaming invocation from reaching the suite's own shell (see fact above), # and /dev/null is devtmpfs, not the virtiofs root the top-level-redirect rule # is about. ev() { _id=$1; _want=$2; _needle=$3; shift 3 _o=$(evtest "$@" 2>&1 -- ev_absent() { _id=$1; _want=$2; _needle=$3; shift 3 _o=$(evtest "$@" 2>&1 /dev/null | wc -l | tr -d ' ')" # --- --version ---------------------------------------------------------------- # version() is `printf("%s %s\n", progname, PACKAGE_VERSION)` and returns # EXIT_SUCCESS, so this is the one path in the program that exits 0 without a # device. Both halves of the line are pinned: the version the ticket names, and # the argv[0] echo that distinguishes progname from the perror literals. ev evtest/version-rc 0 "1.36" --version check_eq evtest/version-number "1.36" "$(evtest --version 2>/dev/null /dev/null &1 so the split is proved: # if the block ever moved to stderr this comes back empty and the case reds. usage_out=$(evtest --bst-bogus-option 2>/dev/null is one of: EV_KEY, EV_SW, EV_LED, EV_SND" --bst-bogus-option # getopt_long writes its own complaint before usage() runs, on stderr. ev evtest/unknown-option-diag 1 "bst-bogus-option" --bst-bogus-option # --- capture mode, no device argument ---------------------------------------- # do_capture(NULL) announces the scan, then scan_devices(). With no event nodes # scandir returns <= 0 and it gives up; with nodes present it prints a menu and # scanf()s -- which reads EOF here. Both routes return NULL, so usage() + rc 1 is # the outcome either way, and this case does not depend on what /dev/input holds. # `evtest` with NO argv at all -- not an empty device string, which would be a # different program path (open("") -> ENOENT). Run once and asserted three ways. nodev_out=$(evtest 2>&1 /dev/null | wc -l | tr -d ' ')" else bst_ok evtest/dev-input-probe printf 'evtest-probe: /dev/input absent\n' fi # --- capture mode, a node that does not exist --------------------------------- # perror("evtest") -- the literal, not argv[0]; see fact 2. ev evtest/missing-node-perror 1 "evtest: No such file or directory" "$NOSUCH" # --- capture mode, a node that is not an input device ------------------------- # /dev/null opens, then print_device_info()'s first ioctl (EVIOCGVERSION) fails # and it perror()s a compound prefix of its own. Asserted in two cases so a # differing strerror string cannot red the branch assertion. if [ -c /dev/null ]; then ev evtest/notinput-branch 1 "evtest: can't get version" /dev/null ev evtest/notinput-errno 1 "Inappropriate ioctl for device" /dev/null else bst_skip evtest/notinput-branch "/dev/null is not a character device on this image" bst_skip evtest/notinput-errno "/dev/null is not a character device on this image" fi # --- query mode: argument arity ---------------------------------------------- # main() takes the device from argv[optind] and then requires two more, so this # message comes from main and not from do_query. ev evtest/query-no-args 1 "Query mode requires device, type and key parameters" --query ev evtest/query-one-arg 1 "Query mode requires device, type and key parameters" --query "$NOSUCH" ev evtest/query-two-args 1 "Query mode requires device, type and key parameters" --query "$NOSUCH" EV_KEY # --- query mode: the event type ---------------------------------------------- ev evtest/query-bad-type 1 "Unrecognised event type: EV_BST_BOGUS" --query "$NOSUCH" EV_BST_BOGUS 1 # A numeric type is looked up BY VALUE, so a type that exists in the kernel but # not in evtest's four-entry table is rejected the same way. EV_ABS is 3. ev evtest/query-type-not-in-table 1 "Unrecognised event type: 3" --query "$NOSUCH" 3 1 # All four names resolve. Reaching perror("open") is the proof: it is the first # thing past every validation step. ev evtest/query-type-ev-key 1 "open: No such file or directory" --query "$NOSUCH" EV_KEY KEY_A ev evtest/query-type-ev-led 1 "open: No such file or directory" --query "$NOSUCH" EV_LED LED_NUML ev evtest/query-type-ev-snd 1 "open: No such file or directory" --query "$NOSUCH" EV_SND SND_BELL ev evtest/query-type-ev-sw 1 "open: No such file or directory" --query "$NOSUCH" EV_SW SW_DOCK # And by value: EV_KEY=1, EV_SW=5, EV_LED=0x11, EV_SND=0x12. strtoul(base 0), so # the hex spellings exercise the same path as the decimal ones. ev evtest/query-type-by-value-1 1 "open: No such file or directory" --query "$NOSUCH" 1 KEY_A ev evtest/query-type-by-value-5 1 "open: No such file or directory" --query "$NOSUCH" 5 SW_DOCK ev evtest/query-type-by-value-0x11 1 "open: No such file or directory" --query "$NOSUCH" 0x11 LED_NUML ev evtest/query-type-by-value-0x12 1 "open: No such file or directory" --query "$NOSUCH" 0x12 SND_BELL # --- query mode: the key name ------------------------------------------------- ev evtest/query-bad-keyname 1 "Unrecognised key name: BST_NOT_A_KEY" --query "$NOSUCH" EV_KEY BST_NOT_A_KEY # A name is looked up only in ITS OWN mode's table, so a valid EV_KEY name is not # a valid EV_SW one. That is what makes the per-mode tables observable. ev evtest/query-keyname-wrong-mode 1 "Unrecognised key name: KEY_A" --query "$NOSUCH" EV_SW KEY_A # Numeric codes take the digit path, decimal and hex alike (KEY_A is 30 = 0x1e). ev evtest/query-key-decimal 1 "open: No such file or directory" --query "$NOSUCH" EV_KEY 30 ev evtest/query-key-hex 1 "open: No such file or directory" --query "$NOSUCH" EV_KEY 0x1e # --- query mode: the bound, for every one of the four maxima ------------------- # do_query rejects `keycode > query_mode->max`, so max itself is IN range and # max+1 is not. This is the runtime half of the patch's compile-time assertion # that KEY_MAX (0x2ff) dominates SW_MAX (0x10), LED_MAX (0x0f) and SND_MAX (0x07): # an in-range case that stopped at the bound, or an out-of-range one that reached # open(2), would mean a max had moved out from under the bitmap's size. ev evtest/bound-key-max 1 "open: No such file or directory" --query "$NOSUCH" EV_KEY 767 ev evtest/bound-key-over 1 "Key 768 is out of bounds." --query "$NOSUCH" EV_KEY 768 ev evtest/bound-sw-max 1 "open: No such file or directory" --query "$NOSUCH" EV_SW 16 ev evtest/bound-sw-over 1 "Key 17 is out of bounds." --query "$NOSUCH" EV_SW 17 ev evtest/bound-led-max 1 "open: No such file or directory" --query "$NOSUCH" EV_LED 15 ev evtest/bound-led-over 1 "Key 16 is out of bounds." --query "$NOSUCH" EV_LED 16 ev evtest/bound-snd-max 1 "open: No such file or directory" --query "$NOSUCH" EV_SND 7 ev evtest/bound-snd-over 1 "Key 8 is out of bounds." --query "$NOSUCH" EV_SND 8 # The bound is checked BEFORE the device is opened -- fact 1, as a case. Same # unopenable path as every case above; if the open came first this would carry # "open: No such file or directory" instead. ev evtest/query-validation-before-open 1 "Key 999 is out of bounds." --query "$NOSUCH" EV_SW 999 ev_absent evtest/query-bounds-not-open 1 "open: No such file" --query "$NOSUCH" EV_SW 999 # --- query mode: the device ------------------------------------------------- ev evtest/query-missing-node 1 "open: No such file or directory" --query "$NOSUCH" EV_KEY KEY_A if [ -c /dev/null ]; then # Opens, then the EVIOCG* ioctl refuses: perror("ioctl"). This is as far into # query_device() as anything reaches without an input device -- the state # bitmap is allocated and handed to ioctl(2) here, and never read. ev evtest/query-notinput-branch 1 "ioctl:" --query /dev/null EV_KEY KEY_A ev evtest/query-notinput-errno 1 "Inappropriate ioctl for device" --query /dev/null EV_KEY KEY_A else bst_skip evtest/query-notinput-branch "/dev/null is not a character device on this image" bst_skip evtest/query-notinput-errno "/dev/null is not a character device on this image" fi # --- a real event node, when the runtime provides one ------------------------- # arch/wasm32/configs/wasm32_defconfig sets CONFIG_INPUT=y, CONFIG_INPUT_EVDEV=y # and CONFIG_VIRTIO_INPUT=y, so an event node is possible on this kernel even # though uinput is not (see below). These are the ONLY cases in the file that # reach the state bitmap query_device() fills and then READ it -- everywhere else # the run ends at open(2) or at the ioctl -- so they are taken opportunistically # rather than waiting on a fixture this image cannot build. NODE=$(ls /dev/input/event* 2>/dev/null | head -1) # real_query # Query mode answers through the exit code alone and prints NOTHING on either # documented outcome: 0 the bit is unset, 10 it is set, 1 an error. Both 0 and # 10 are accepted -- which one a live device gives is a fact about the device, # not about evtest -- and the silence is asserted beside the code, because a # message here would mean an error path that happened to exit 0. real_query() { _rid=$1; _rtype=$2; _rkey=$3 if [ -z "$NODE" ] || [ ! -c "$NODE" ]; then bst_skip "$_rid" "no /dev/input/event* node on this runtime, so the state bitmap is filled but never read" return fi _ro=$(evtest --query "$NODE" "$_rtype" "$_rkey" 2>&1 /dev/null && UIMOD=/proc/misc UIREASON="no uinput on this kernel (arch/wasm32/configs/wasm32_defconfig sets CONFIG_INPUT_EVDEV=y and CONFIG_VIRTIO_INPUT=y but no CONFIG_INPUT_UINPUT) -- measured: node=$UIN /proc/misc=$UIMOD" if [ "$UIN" != absent ]; then # Reached only on a runtime that grows uinput; the cases are written the day # that happens rather than guessed at now, because what a synthetic device # reports is a property of the driver, not of evtest. bst_skip evtest/uinput-capability-dump "uinput node $UIN is present but this suite has no fixture for it yet" bst_skip evtest/uinput-query-set "uinput node $UIN is present but this suite has no fixture for it yet" bst_skip evtest/uinput-query-unset "uinput node $UIN is present but this suite has no fixture for it yet" else bst_skip evtest/uinput-capability-dump "$UIREASON" bst_skip evtest/uinput-query-set "$UIREASON" bst_skip evtest/uinput-query-unset "$UIREASON" fi