Agile2014 Demo – Mocking Device Drivers with GoogleMock

This week, our Agile2014 SW/HW co-development demo took a step closer to the hardware. But considering the real hardware isn’t going to be here for another month or so, we had to get creative to test the code that interacts with the device drivers.

Mocking is a technique I’m starting to get comfortable with to isolate and test specific functions. I’m also getting more comfortable with GoogleMock. It’s only taken 3 weeks, not that I’m an expert or anything, but the basics are pretty straightforward. However, while the basics apply very well to C++ code, they don’t apply as well to C code. Here’s a “warning” on the GoogleMock FAQ to explain…

My code calls a static/global function. Can I mock it?

You can, but you need to make some changes.

In general, if you find yourself needing to mock a static function, it’s a sign that your modules are too tightly coupled (and less flexible, less reusable, less testable, etc). You are probably better off defining a small interface and call the function through that interface, which then can be easily mocked. It’s a bit of work initially, but usually pays for itself quickly.

So GoogleMock cannot be used to mock C functions directly. Though as the FAQ states, there’s a workable solution if you can re-jig your code. I ended up sticking with GoogleMock but going in a different direction. We’re starting with code from a reference design that I don’t want to change. We’re not comfortable with the hardware or the device drivers yet so re-jigging code this early, even though it’s not overly complicated, is something we wanted to stay away from… for now anyway.

This is how we’ve chosen to mock the device drivers. We’ve taken the Xilinx device driver header files as is. There’s only a manageable few thus far, about a half dozen in all. From the header files we created a stub implementation of each function. For example, the I2C driver has a function called XIic_DynInit with a prototype declared in the xiic_l.h header as…

Screen Shot 2014-05-08 at 8.13.02 PM

The stub implementation that I started with (not brain surgery here) looks like this…

Screen Shot 2014-05-08 at 8.15.13 PM

After pulling in the xiic_l.h header file and the stub, I made sure everything compiled to catch any silly mistakes. I did make a couple so I fixed them and moved on.

Defining the stub was only the first step. Next, we want to insert real functionality in the stub that we can use to mimic the functionality of the device driver. That’s where GoogleMock comes in. Even though we can’t use it with global functions, we chose to map global device driver functions to an instance of a mock object. For that, we define a mock object with a method for the XIic_DynInit function as an example…

Screen Shot 2014-05-08 at 8.21.27 PM

In our new header file for the mock object, we use the MOCK_METHOD1 macro from GoogleMock to create all the relevant XIic_DynInit plumbing. You’ll see we also have global functions for instantiating and destroying the mock object called getXdriverMock() and destroyXdriverMock(). Those methods look like this in the implementation file…

Screen Shot 2014-05-08 at 9.00.20 PM

So we have stubs for the global device driver methods and we have a mock object. The final piece is connecting the dots between the 2. To do that, we go back to our device driver stubs and call the corresponding method on our mock object. For the XIic_DynInit function, that looks like this…

Screen Shot 2014-05-08 at 9.00.39 PM

With that, any calls to the global XIic_DynInit function are redirected to the mock. Now we have full control over the device driver functionality for our unit tests and we haven’t had to change any of the code we inherited that uses the device drivers. Here’s a unit test for our I2C controller that as an example…

Screen Shot 2014-05-08 at 8.38.24 PM

That test is for the init() method of the I2C controller. On the first line, we’re using the EXPECT_CALL to say the XIic_DynInit() global function should be called once during the test with an input value equal to the HDMI I2C base address. The second line is the call to the init() method. If the init() method does as we expect and calls the Xiic_DynInit function, we’ve got a passing test. If not, the test fails and our init() method needs some work!

Last thing to see is the instantiation and destruction of the mock object. That’s done in the constructor and destructor of the test fixture…

Screen Shot 2014-05-08 at 9.02.47 PM

…and that’s how a rookie/pretend software developer uses GoogleMock to avoid changes to legacy code that uses global C functions from a Xilinx device driver :). I’m no pro, so I can’t say whether or not this is the ideal way. On the one hand I do recognize that this solution isn’t portable in the way that the GoogleMock FAQ suggests is possible. On the other, this was quite easy to put together and using it so far has been a snap. Time will tell whether or not we continue down this path or choose a different approach. If you’ve got any better ideas, I’d love to hear them… especially since we’ve got a long way to go until we’re done. It’s not too late to change!

-neil

Leave a Reply

Your email address will not be published. Required fields are marked *