tests: hotplug API and virtual-device hotplug scenarios - #826
Conversation
Two new test tiers for the hotplug API: - test_hotplug_api.c (HotplugAPI_<backend>): argument validation, callback-handle properties, implicit init, hid_exit() teardown and register/deregister thread churn. Needs no device or privileges, so it runs against every backend in the ordinary CI matrix. - test_hotplug.c (Hotplug_<backend>): device-backed hotplug scenarios (async delivery, exactly-once ENUMERATE pass, callback-return deregistration, pass-before-live ordering, ARRIVED/LEFT payloads, filtering, dispatch order, deregistration post-condition and re-entrant registration) against a virtual device whose presence is toggled with the new test_virtual_device_unplug()/_replug() calls. Implemented for the uhid provider (UHID_DESTROY/UHID_CREATE2 on the same fd); the other providers return TEST_VDEV_UNAVAILABLE and their hotplug tests self-skip until presence toggling is implemented. Assisted-by: claude-code:claude-fable-5
Make the tier-2 Hotplug_<backend> test drive real plug/unplug on two more providers so it exercises the async backends instead of self-skipping. libusb (raw_gadget VM): the provider now unbinds/rebinds the gadget from the dummy UDC (close the raw-gadget fd to disconnect; reopen + INIT + RUN to reconnect), producing real libusb LEFT/ARRIVED events. Windows (vhidmini UMDF driver): the provider locates the root devnode by its INF hardware id (the instance path is PnP-derived from the setup class, not knowable a priori) and toggles presence by disabling/enabling the child HID PDO, so the UMDF host stays up and the HID interface cleanly drops and returns. The device identity is aligned with the static driver (primary PID 0x9001 and serial "HIDAPI-HOTPLUG-TEST" on Windows; other platforms keep 0x9002), and create() reports UNAVAILABLE for a device that is not present so the second-device sub-test skips. cfgmgr32 is linked for the winapi provider targets. Both run only under the ci-virtual-device label (raw_gadget VM / installed driver) and skip cleanly elsewhere. Assisted-by: claude-code:claude-opus-4-8
Full implementation review (fresh pass, head
|
Publish T15's results before the flag the main thread waits on, relax the handle-uniqueness assertion to match the documented contract, make the rawgadget teardown signal both workers until both exit and guard rg_plug against a redundant plug, re-enable a left-disabled HID child before the Windows provider's presence probe, and refresh the docs that still said only uhid can toggle presence. Assisted-by: claude-code:claude-opus-5
The already-plugged guard added in the previous commit mistook the calloc'd fd 0 for an open gadget fd, so the first plug closed the process's stdin; initialise fd to -1 before rg_plug() sees it. Also set ep0_exited on ep0_thread_fn's allocation-failure return, so teardown does not spend its whole signalling budget on a thread that already left. Assisted-by: claude-code:claude-opus-5
Re-review of fix commits
|
tests-1: apply test and harness review findings tests-2: apply test and harness review findings tests-3: apply test and harness review findings tests-4: apply test and harness review findings tests-5: apply test and harness review findings tests-6: apply test and harness review findings tests-7: apply test and harness review findings tests-8: apply test and harness review findings tests-9: apply test and harness review findings tests-10: apply test and harness review findings tests-11: apply test and harness review findings tests-12: apply test and harness review findings tests-13: apply test and harness review findings tests-14: apply test and harness review findings tests-15: apply test and harness review findings tests-16: apply test and harness review findings tests-17: apply test and harness review findings tests-18: apply test and harness review findings tests-19: apply test and harness review findings tests-20: apply test and harness review findings tests-21: apply test and harness review findings tests-22: apply test and harness review findings tests-23: apply test and harness review findings tests-24: apply test and harness review findings tests-25: apply test and harness review findings tests-26: apply test and harness review findings tests-27: apply test and harness review findings tests-28: apply test and harness review findings tests-29: apply test and harness review findings tests-30: apply test and harness review findings tests-31: apply test and harness review findings tests-32: apply test and harness review findings tests-33: apply test and harness review findings tests-34: apply test and harness review findings tests-35: apply test and harness review findings tests-36: apply test and harness review findings tests-37: apply test and harness review findings tests-38: apply test and harness review findings tests-39: apply test and harness review findings docs-3: apply test and harness review findings docs-4: apply test and harness review findings docs-16: apply test and harness review findings docs-17: apply test and harness review findings docs-18: apply test and harness review findings docs-19: apply test and harness review findings docs-20: apply test and harness review findings docs-21: apply test and harness review findings docs-22: apply test and harness review findings docs-23: apply test and harness review findings docs-38: apply test and harness review findings docs-39: apply test and harness review findings docs-40: apply test and harness review findings docs-41: apply test and harness review findings docs-42: apply test and harness review findings docs-43: apply test and harness review findings docs-44: apply test and harness review findings docs-45: apply test and harness review findings docs-46: apply test and harness review findings docs-61: apply test and harness review findings docs-62: apply test and harness review findings Assisted-by: codex-cli:gpt-6-astra
tests-r2-1: Limit truncation failures to test-device payloads. tests-r2-2: Gate exit delivery and use a fresh post-init counter. tests-r2-3: Validate cancellation before retrying target cleanup. tests-r2-4: Skip candidates that disappear during devnode queries. tests-r2-5: Correct Windows provider creation comments. Assisted-by: codex-cli:gpt-6-astra
…mption - rename the shadowed serial-narrowing loop variable (C4456) - replace getenv() with a Win32-safe helper (C4996 under /W4 /WX) - T15: both callbacks must run off the registering thread, but the Windows backend delivers from a threadpool / CM notification thread, so the two invocations need not share one thread id - document why cb_publication may read the registering thread's handle - libusb-vhid CI: fetch dummy_hcd.c through the authenticated contents API with retries; anonymous raw.githubusercontent.com fetches hit 429 Assisted-by: claude-code:claude-fable-5-1
Fresh review pass (2026-09-08)Two independent reviews (Claude Fable 5.1 and gpt-6-astra), merged and deduplicated: 39 findings (1 major, 20 minor, 11 nit, 7 note). Fixes landed in three commits:
Final adversarial verification (Fable, on cd008b0): 54 fixed, 2 partial (one base-merge item, one whose premise turned out wrong), 6 skipped as other-PR items. It also claimed a major: that the Windows virtual-device job cannot load the ASan runtime without an MSVC developer-environment step. Run 34273898724 contradicts that (the ASan-instrumented DeviceIO and HotplugAPI binaries ran and passed there), so no workflow change was made. CI: the plain Builds matrix on this branch alone fails by design (the tests encode the new contract and run against the base backends). On the aggregate branch (#830) Builds is green and the device-backed suites pass on both Windows and libusb. Drafted with Claude Code. |
Adds the test suite for the hotplug API (#764 / connection-callback), in two tiers, built on the virtual-device test harness from #815.
What is in here
src/tests/test_hotplug_api.c— tier 1,HotplugAPI_<backend>: everything in the hotplug contract that is observable without a device event (argument validation, callback-handle properties, implicithid_init(),hid_exit()teardown incl. a register→immediate-hid_exitstress loop, and a 2-thread register/deregister churn). Needs no device, no privileges: registered for all four backends and runs in the ordinary per-push CI matrix. Self-skips (77) when a backend reports hotplug as unsupported at runtime (e.g. libusb withoutLIBUSB_CAP_HAS_HOTPLUG).src/tests/test_hotplug.c— tier 2,Hotplug_<backend>: device-backed scenarios: async delivery, exactly-once ENUMERATE pass, callback-return deregistration (live + mid-pass), pass-before-live ordering, ARRIVED/LEFT payloads and path correlation, LEFT-without-ENUMERATE, VID/PID filtering, registration-order dispatch, the deregistration post-condition, and register/deregister from within a callback.test_virtual_device_unplug()/test_virtual_device_replug()— new provider calls that toggle device presence without destroying the provider context. Implemented for uhid (UHID_DESTROY/UHID_CREATE2), rawgadget (unbind/rebind the gadget from the dummy_hcd UDC) and the Windows vhidmini provider (disable/enable the HID child devnode); the IOHIDUserDevice provider still returns TEST_VDEV_UNAVAILABLE, so only Hotplug_darwin self-skips.The tier-2 test follows a strict synchronization discipline (see
src/tests/README.md): callbacks only deep-copy events into a lock-protected log; every expectation is a deadline-based predicate poll; absence assertions sit behind event barriers, not time windows; no retries.Where each test runs
HotplugAPI_hidraw,HotplugAPI_libusbbuilds.ymlubuntu-cmakeHotplugAPI_winapibuilds.ymlwindows-cmake (MSVC, NMake, ClangCL, MinGW)HotplugAPI_darwinbuilds.ymlmacos-cmakeHotplug_hidrawbuilds.ymlubuntu-cmake (viamodprobe uhid+sudo ctest)Hotplug_libusbHotplug_winapiHotplug_darwinThe behavioral expectations encode the async-ENUMERATE contract, i.e. they require #822, #823, #824 and #825. This PR is no longer draft. CI is green on the
hotplug-integrationbranch (all four backends + this PR) and will be green here once #822-#825 merge. Until then, tier 1 remains red onconnection-callbackas-is (e.g.*callback_handleis not zeroed on a failed registration yet), and the tier-2 async-ENUMERATE expectations still require those backend changes.Current validation: tier-1 HotplugAPI_* per push on all four backends; tier-2 Hotplug_hidraw per push (uhid), Hotplug_libusb and Hotplug_winapi in the label-gated ci-virtual-device jobs, Hotplug_darwin self-skips.
Relates to: #764
Assisted-by: claude-code:claude-fable-5
Assisted-by: claude-code:claude-opus-4-8
Assisted-by: claude-code:claude-opus-5
Assisted-by: claude-code:claude-fable-5-1
Assisted-by: codex-cli:gpt-6-astra