Skip to content

tests: virtual HID device test harness (Linux/Windows/macOS/libusb)#815

Draft
Youw wants to merge 2 commits into
masterfrom
virtual-device-tests
Draft

tests: virtual HID device test harness (Linux/Windows/macOS/libusb)#815
Youw wants to merge 2 commits into
masterfrom
virtual-device-tests

Conversation

@Youw
Copy link
Copy Markdown
Member

@Youw Youw commented Jun 7, 2026

Summary

Adds a small, reusable virtual HID device test harness so HIDAPI can be tested without physical hardware. Tests are written purely against the public HIDAPI API plus a backend-agnostic test_virtual_device interface, so the same test runs against every backend that ships a provider.

Devices expose pre-recorded scenarios: the test sends a Feature report whose first payload byte is a command, and the device replays the matching canned input report — keeping the test code 100% platform-neutral.

Intended as a foundation further tests can build on (e.g. the hid_read_interrupt test in #799 can rebase onto this harness).

What's here

  • src/tests/test_device_io.c — a simple open → write → exchange reports (Feature trigger / input read) → close smoke test.
  • Four virtual-device providers, each self-skipping (CTest code 77) when its device can't be created:
Platform / backend Provider Mechanism CI
Linux / hidraw test_virtual_device_uhid.c kernel /dev/uhid runs + passes every push (ubuntu-cmake)
Linux / libusb test_virtual_device_rawgadget.c /dev/raw-gadget + dummy_hcd, inside a VM self-skips per push; runs + passes in the manual libusb-vhid-test (VM)
Windows / winapi test_virtual_device_win.c + windows/driver/ modified vhidmini2 UMDF2 driver self-skips per push; runs + passes in the manual win-vhid-test
macOS / darwin test_virtual_device_mac.c IOHIDUserDevice (IOKit) self-skips per push (macos-cmake); runs on a real Mac

See src/tests/README.md for details, including why FreeBSD/NetBSD/OpenBSD have no provider (no userspace HID/USB-create facility on any BSD).

CI

  • Per-push (builds.yml): the test is built on all platforms and run where possible. ubuntu-cmake loads uhid and runs DeviceIO_hidraw for real, while DeviceIO_libusb builds but self-skips there (the hosted kernel has no USB gadget subsystem); Windows and macOS likewise build the test and self-skip — so the matrix stays green everywhere.
  • win-vhid-test.yml (manual): builds, self-signs and installs the vhidmini2 driver on a hosted windows-latest runner and runs DeviceIO_winapi against the real device. ✅ verified passing.
  • libusb-vhid-test.yml (manual): the hosted ubuntu-latest (azure) kernel has no USB gadget subsystem, so this runs DeviceIO_libusb inside a lightweight virtme-ng + QEMU VM that boots a generic kernel (building dummy_hcd out-of-tree and loading raw_gadget), against a real virtual USB device. ✅ verified passing. The same approach works locally and on WSL2.

The two privileged jobs run only via workflow_dispatch or when a pull request carries the ci-virtual-device label, so they stay out of the per-push matrix.

Commits

  1. tests: add a backend-agnostic virtual HID device test harness
  2. ci: build and run the virtual HID device tests

Drafted with Claude Code.

@Youw Youw added the ci-virtual-device Run the virtual HID device CI jobs (Windows driver + libusb raw-gadget) label Jun 7, 2026
@mcuee
Copy link
Copy Markdown
Member

mcuee commented Jun 7, 2026

Great. We need this kind of virtual test device for hidapi (and also libusb).

@Youw Youw force-pushed the virtual-device-tests branch from 1cdc8d5 to b330600 Compare June 7, 2026 11:54
@mcuee
Copy link
Copy Markdown
Member

mcuee commented Jun 7, 2026

@Youw

FYI on WDK, the Windows runners have WDK installed already.

Nuget WDK -- smaller but lacking some tools, which may be okay for this purpose
Built-in WDK -- good enough
EWDK -- great for local build of libusb-win32/libusbK but not good for CI.

Reference:

@Youw Youw force-pushed the virtual-device-tests branch 2 times, most recently from 5a24890 to 6089973 Compare June 7, 2026 13:51
@Youw
Copy link
Copy Markdown
Member Author

Youw commented Jun 7, 2026

Thanks @mcuee, that's useful. The win-vhid-test job currently downloads WDK 26100.6584 explicitly because the first preinstalled option I tried (the Chocolatey windowsdriverkit11 VSIX) was VS2019-only, so msbuild under the VS2022 toolset couldn't find wdf.h. Good to know the built-in (or NuGet) WDK should be enough — I'll try pointing the job at the preinstalled/NuGet WDK to drop that ~2–3 min download and the external fetch, and confirm the WDF headers resolve against the VS2022 toolset. Will report back.

This reply was generated by Claude Code at my request.

@Youw
Copy link
Copy Markdown
Member Author

Youw commented Jun 7, 2026

This reply was generated by Claude Code at my request.

https://www.instagram.com/p/DXcOy5riYPS/

(I home community don't mind a little humor here)

@Youw Youw force-pushed the virtual-device-tests branch 2 times, most recently from d3877f1 to 35b5186 Compare June 7, 2026 19:41
@Youw
Copy link
Copy Markdown
Member Author

Youw commented Jun 7, 2026

Followed up on the WDK tip — thanks again. On the current windows-latest image the runner ships the Windows SDK (10.0.26100) but not the WDK: there are no UMDF (wdf) headers under Windows Kits\10\Include, so the vhidmini2 UMDF2 build still needs the WDK installed.

So rather than dropping the download, I cached it. The win-vhid-test job now caches a self-contained offline WDK installer layout (actions/cache@v4, keyed by WDK version) and installs from it — the WDK is downloaded only once and restored from cache on later runs. Verified: on a cache hit the download step is skipped, the driver builds, and DeviceIO_winapi passes. I also kept a preinstalled-WDK fast-path, so if a future image ships the WDK with UMDF headers it's used automatically with no download.

(The NuGet WDK would be a cleaner long-term route, but it needs reworking the legacy .vcxproj, so I left that for later.)

Posted by Claude Code at my request.

@Youw Youw force-pushed the virtual-device-tests branch from 35b5186 to a0792ee Compare June 7, 2026 21:09
Youw added 2 commits June 8, 2026 00:26
Add a reusable harness that runs HIDAPI tests against a *virtual* HID device,
needing no physical hardware. Tests use only the public HIDAPI API plus a
backend-agnostic test_virtual_device interface, so the same test runs against
every backend that ships a virtual-device provider. Devices expose pre-recorded
"scenarios": the test sends a Feature report carrying a command byte and the
device replays the matching canned input report.

- src/tests/test_device_io.c: a simple open -> write -> exchange reports ->
  close smoke test (skips via CTest code 77 when no virtual device is present).
- Providers:
    test_virtual_device_uhid.c       Linux hidraw  (kernel /dev/uhid)
    test_virtual_device_rawgadget.c  Linux libusb  (/dev/raw-gadget + dummy_hcd)
    test_virtual_device_win.c (+windows/driver/)   Windows winapi (vhidmini2 UMDF2)
    test_virtual_device_mac.c        macOS darwin  (IOHIDUserDevice)
  Each self-skips when its device can't be created.
- HIDAPI_WITH_TESTS is offered on Linux/macOS too; src/tests builds the test for
  whichever backend(s) are present.
- src/tests/README.md documents the providers, the scenario protocol, and why
  FreeBSD/NetBSD/OpenBSD have no provider (no userspace HID/USB-create facility).
- src/tests/windows/driver/ vendors a modified Microsoft vhidmini2 UMDF2 sample
  under its original Microsoft Public License (MS-PL); LICENSE.txt and README.md
  there record the provenance and modifications and keep it separate from
  HIDAPI's own license (the driver is a standalone test fixture, not linked into
  the library).

Assisted-by: Claude:claude-opus-4.8
- builds.yml: build the test on Linux/Windows/macOS and run it where possible
  (ubuntu-cmake loads uhid and runs DeviceIO_hidraw; Windows/macOS/libusb build
  and self-skip), keeping the per-push matrix green.
- win-vhid-test.yml: build, self-sign and install the vhidmini2 driver on a
  hosted windows-latest runner and run DeviceIO_winapi against it.
- libusb-vhid-test.yml (+ .github/vmrun-libusb.sh): the hosted kernel has no USB
  gadget subsystem, so run DeviceIO_libusb inside a virtme-ng VM that boots a
  generic kernel (building dummy_hcd out-of-tree and loading raw_gadget) against
  a real virtual USB device.

Both privileged jobs run only via workflow_dispatch or a 'ci-virtual-device'
pull-request label, so they stay out of the per-push matrix.

Assisted-by: Claude:claude-opus-4.8
@Youw Youw force-pushed the virtual-device-tests branch from a0792ee to 3ae4b91 Compare June 7, 2026 21:26
@Youw Youw requested a review from mcuee June 8, 2026 11:38
@Youw
Copy link
Copy Markdown
Member Author

Youw commented Jun 8, 2026

I see two general issues with this PR:

  • we're including MS-licensed code, even though it is only used for testing, even though we're not linking against it and not shipping it nor artifacts; not sure if this could be an issue (probably not for HIDAPI itself, but might be relevant to some other projects that using HIDAPI as a subproject)
  • the amount of code generated by Claude is significant and I'm not in capacity to actually review and understand all of it; but taking this is a test code and development driven by defining the expected outcome/result first - I'm still leaning towards accepting it, of course with remark that if anytihng breaks, me (Claude of course) will take care/fix it. It not really compfortable with this aproach, and not planning on doing such thing (accepting w/o fully reviewing/understanding) for actuall library/production code, but this still worth mentioning.

CC: @JoergAtGithub - not sure if your'e watching this issue, you might be interested in this in general.

@mcuee
Copy link
Copy Markdown
Member

mcuee commented Jun 8, 2026

  1. we're including MS-licensed code, even though it is only used for testing, even though we're not linking against it and not shipping it nor artifacts; not sure if this could be an issue (probably not for HIDAPI itself, but might be relevant to some other projects that using HIDAPI as a subproject)

I do not see this as an issue. Just need to mention it clearly. They can just skip the test codes.

the amount of code generated by Claude is significant and I'm not in capacity to actually review and understand all of it; but taking this is a test code and development driven by defining the expected outcome/result first - I'm still leaning towards accepting it, of course with remark that if anytihng breaks, me (Claude of course) will take care/fix it. It not really compfortable with this aproach, and not planning on doing such thing (accepting w/o fully reviewing/understanding) for actuall library/production code, but this still worth mentioning.

Again I think this is okay for this testing codes. I agree this should not be the case for the core library codes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-virtual-device Run the virtual HID device CI jobs (Windows driver + libusb raw-gadget)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants