Driver regression testing with roadtest
One of the problems with hardware is its sheer variety. Consider a device as conceptually simple as a GPIO port which, at its core, drives a single electrical line to either a logical true or false value. GPIO drivers should be simple things, and many of them are, but vendors like to add their own flourishes with each new release. As a result, there are well over 150 GPIO drivers in the kernel source, many of which can drive more than one variant of a device. There is no way to build a system with all of those devices in it; most of them are part of a larger peripheral or system-on-chip, and many of them have not been commercially available for years.
Of course, each of those drivers was, at one point, tested on the hardware it drives. They would normally be expected to continue to work. But the kernel is constantly changing, and changes often affect drivers as well. Developers making those changes do their best to avoid breaking anything, but they have no way to test changes that affect most drivers; even subsystem maintainers will normally only have a subset of the devices available for testing. So there is always a possibility that regressions will slip in and go unnoticed until somebody's device stops working.
Roadtest aims to circumvent this problem by eliminating the need to actually have the hardware present to test whether a driver still works. This is done by pairing driver tests with mock devices that can run anywhere; when a developer makes a set of regression tests for a specific driver, that work includes the mocked version of the target device(s) as well. The tests are then run under a specially built User-Mode Linux kernel, with the mocked hardware filling in for the real thing.
Forcing a test author to also implement an emulated version of the device under test sounds like a high bar to overcome. The good news is that the mocked devices need not encapsulate the full complexity of the real thing; they simply need to respond well enough to verify that the driver is behaving in the expected way. Emulating the device's programming interface (without actually doing the things a real device would do) may well be sufficient.
Consider, for example, this test from the patch set, which verifies the driver for the opt3001 light-sensor driver. Both the tests and the mocked devices are written in Python; the core part of the implementation for the mocked opt3001 device looks like this:
class OPT3001(SMBusModel): def __init__(self, **kwargs: Any) -> None: super().__init__(regbytes=2, byteorder="big", **kwargs) # Reset values from datasheet self.regs = { REG_RESULT: 0x0000, REG_CONFIGURATION: 0xC810, REG_LOW_LIMIT: 0xC000, REG_HIGH_LIMIT: 0xBFFF, REG_MANUFACTURER_ID: 0x5449, REG_DEVICE_ID: 0x3001, } def reg_read(self, addr: int) -> int: val = self.regs[addr] if addr == REG_CONFIGURATION: # Always indicate that the conversion is ready. This is good # enough for our current purposes. val |= REG_CONFIGURATION_CRF return val def reg_write(self, addr: int, val: int) -> None: assert addr in self.regs self.regs[addr] = val
The opt3001 is an SMBus device, programmable via writes to (and reads from) a set of registers. Using the SMBus emulation provided with roadtest, this mock device simply implements a handful of registers. It is hard to imagine a simpler implementation; the read side doesn't even bother to check whether a requested register number is valid, presumably on the assumption that the crash resulting from a bad read request would say "test failure" with adequate volume.
The roadtest framework will take the mock device implementation and connect it to the driver (in the User-mode Linux instance) as if it were a real device. The test itself runs as a user-space process in that instance; it tweaks some of those registers to simulate the arrival of data, then reads that data using the IIO API:
def test_illuminance(self) -> None: data = [ # Some values from datasheet, and 0 (0b_0000_0000_0000_0000, 0), (0b_0000_0000_0000_0001, 0.01), (0b_0011_0100_0101_0110, 88.80), (0b_0111_1000_1001_1010, 2818.56), ] with self.driver.bind(self.dts["addr"]) as dev: luxfile = dev.path / "iio:device0/in_illuminance_input" for regval, lux in data: self.hw.reg_write(REG_RESULT, regval) self.assertEqual(read_float(luxfile), lux)
The register writes (the self.hw.reg_write() call above) go straight to the mock device. The reads, instead, are directed to the driver, which will interact with the mock device to obtain the requested data. If the driver is working properly, it will read the simulated data from the mock device and return the results that the test is expecting.
This is a simple test; more complex tests could verify that the driver is setting up the hardware correctly, dealing with error conditions, and so on. Even so, there would appear to be limits to a mechanism like this; it will be difficult to use it to verify that, say, a Video4Linux driver is correctly managing the buffer queue when user-mapped buffers are in use with a planar YUV color scheme. But for simpler devices, of which there are many, a system like roadtest may provide a level of assurance that kernel developers currently do not have.
A lot more information on roadtest can be found in this documentation patch, which includes a tutorial on adding a test for a new device. The patch set as a whole contains tests for a few device types; presumably that list would grow considerably if this framework were to be merged into the mainline.
There have not been a lot of comments on the system so far, so it is hard
to be sure about what roadtest's prospects for merging are. Brendan
Higgins was clear
enough on his opinion of roadtest, though: "I love the framework;
this looks very easy to use
". Testing frameworks like roadtest
should not bother anybody who does not choose to use them and, if they are
made comprehensive enough, they can significantly increase the chances of
catching regressions before they get into a released kernel. So it is hard
to see a reason why roadtest wouldn't eventually become part of the
mainline kernel — unless, of course, kernel developers would really rather
not lose an excuse justifying the lack of regression testing for drivers.
Index entries for this article | |
---|---|
Kernel | Development tools |
Posted Mar 19, 2022 0:55 UTC (Sat)
by pmulholland (subscriber, #124686)
[Link]
I’ve designed and used proprietary frameworks like this for embedded systems testing (bare metal, RTOS and embedded Linux), and the complexity of mocking the hardware can really be chosen to be almost as shallow as the register and interrupt interface, or you can build out an entire virtual device, or something in between.
As an idea of how powerful python based regression testing can be, with some abstraction I’ve emulated I2C, USB, and even PCI devices using a few lines of python, with the kernel booting up and discovering them.
Posted Mar 19, 2022 6:44 UTC (Sat)
by pabs (subscriber, #43278)
[Link] (10 responses)
Posted Mar 19, 2022 8:42 UTC (Sat)
by johill (subscriber, #25196)
[Link] (9 responses)
Which, sadly, is also lacking, but especially Anton has been working on it, and I still have a task in my backlog to describe all the stuff they're also using here (time-travel & virtio/vhost-user).
A couple of people also use it all the time (kunit, in wifi we use it for the hostap tests [1], etc.) so it's at least constantly maintained to work for those use cases.
Posted Mar 19, 2022 13:42 UTC (Sat)
by pabs (subscriber, #43278)
[Link] (8 responses)
https://sources.debian.org/src/user-mode-linux/sid/debian...
Posted Mar 19, 2022 20:54 UTC (Sat)
by johill (subscriber, #25196)
[Link] (7 responses)
Posted Mar 20, 2022 0:42 UTC (Sun)
by pabs (subscriber, #43278)
[Link] (6 responses)
Also, I see there is also a DocBook based linux(1) manual page and of course the Kconfig files duplicate the upstream ones:
https://sources.debian.org/src/user-mode-linux/5.16um1/li...
Posted Mar 21, 2022 11:23 UTC (Mon)
by johill (subscriber, #25196)
[Link] (5 responses)
Posted Mar 22, 2022 1:02 UTC (Tue)
by pabs (subscriber, #43278)
[Link] (4 responses)
Posted Mar 24, 2022 10:37 UTC (Thu)
by riteshsarraf (subscriber, #11138)
[Link] (2 responses)
Posted Mar 24, 2022 10:51 UTC (Thu)
by pabs (subscriber, #43278)
[Link] (1 responses)
https://kernel-team.pages.debian.net/kernel-handbook/ Posted Mar 24, 2022 12:14 UTC (Thu)
by johill (subscriber, #25196)
[Link]
Posted Mar 19, 2022 10:46 UTC (Sat)
by roc (subscriber, #30627)
[Link] (1 responses)
Posted Mar 29, 2022 11:34 UTC (Tue)
by qwertyface (subscriber, #84167)
[Link]
Posted Apr 1, 2022 15:31 UTC (Fri)
by andy_shev (subscriber, #75870)
[Link]
Posted Apr 4, 2022 14:16 UTC (Mon)
by arnout (subscriber, #94240)
[Link]
There was a talk at FOSDEM discussing a completely different approach called EasyMock. Rather then using UML, it mocks away all of the kernel itself. It's not so great for regression testing of changes outside of the driver (because exactly those things are mocked away), but I think it has much more potential for testing the implementation of the driver itself, because it's much easier to mock corner cases and error paths (e.g. failing allocations).
With roadtest, I expect things like error paths for allocations will remain untested because they can't be mocked.
Driver regression testing with roadtest
Driver regression testing with roadtest
Driver regression testing with roadtest
Driver regression testing with roadtest
https://tracker.debian.org/pkg/user-mode-linux
I wasn't (and we should probably discuss this on the uml list), but:
Driver regression testing with roadtest
Driver regression testing with roadtest
https://sources.debian.org/src/user-mode-linux/5.16um1/co...
https://sources.debian.org/src/user-mode-linux/5.16um1/co...
Driver regression testing with roadtest
Driver regression testing with roadtest
Driver regression testing with roadtest
Driver regression testing with roadtest
Driver regression testing with roadtest
Driver regression testing with roadtest
There's a really nice property-based testing framework for Python called Hypothesis. I would imagine that in some cases using it to generate test-cases (register values etc.) would work quite nicely within Roadtest.
Driver regression testing with roadtest
Driver regression testing with roadtest
Driver regression testing with roadtest