Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make unit tests for embedded code?

I'm working on software for a Cortex-M4 based microcontroller in C++. I have a lot of code (drivers, etc.) that is highly machine dependent. And I have higher level code which is closely dependant on the low level code by using the drivers directly. Example: a low-level part is eg. a UART driver which is very hardware-specific, and a high-level part is a communication protocol which is based on UART. (This software runs on "bare-metal", ie. there is no operating system underneath.)

This code is currently tightly coupled, thus not unit testable.
I'd like to make it testable.

So I figured that I'd create an abstraction of the low-level parts, and make the high-level parts depend only on the abstraction. I could then create mocks of the abstraction which would be used by the unit tests, and a real implementation which would run on the microcontroller.

  • Is this a correct approach?
  • How can I create such an abstraction?
    Most of the sources I've found strongly discourage the use of inheritance and virtual functions in embedded systems. What other ways are there?

So, in summary, I'd like to create a hardware abstraction layer (HAL), but I'm asking how to do it? Should I use virtual inheritance in C++, or is there another, better way?

like image 962
Venemo Avatar asked Jul 19 '17 15:07

Venemo


2 Answers

For C++ I would suggest using an interface so say we have a HAL.hpp and in there we define our pure virtual functions we want to implement:

class HAL
{
    virtual void func1() = 0;
    virtual void func2() = 0;
};

Then you can have your Mock.cpp implement this and you can have a Real.cpp implement the same:

Mock.cpp:
class Mock : HAL
{
        virtual void func1(){ }
        virtual void func2(){ }
}

Now the other way is you define your functions in HAL.h like and provide no implementation here:

void func1();
void func2();

You then create a HAL.cpp and add functionality there you wish to see on the target. Create all of this as library called HAL. Link this library to your main project.

Now for mocking and testing. Create a seperate project for your tests. Add the sources you want to test but do not link the HAL library. Instead create another source file Mock.cpp include the HAL.h and provide implementation for it. In this way instead of your functionality from the HAL library the Mock's implementation would be called.

like image 110
Samer Tufail Avatar answered Sep 28 '22 02:09

Samer Tufail


Create the microcontroller HAL in separate files from the mock HAL. For the microcontroller, include the microcontroller HAL sources in your project. For the unit test system, include the mock HAL sources in your project.

You can also test on-target by using compiler macro defines to switch in pieces of the mock HAL, and switch out pieces of the microcontroller HAL.

You can even use your debugger to force values at interface points to trigger all paths; doing this with a code coverage tool will let you know if you've exercised all paths (and MC/DC if you need it). This is sometimes the only way to simulate hardware failures or exceptional conditions.

like image 22
Doug Currie Avatar answered Sep 25 '22 02:09

Doug Currie