2

I am writing unit tests for some embedded C, that run on a host machine (not testing on target yet) and compile with GCC. I have been using the Ceedling build system with the Unity testing framework to test.

One of the files I would like to test includes a file (say a.h) that includes another file (say, cpu.h) that is part of the board support package from the embedded device vendor, and uses keywords specific to the target compiler (e.g. __cregister, such as in extern __cregister volatile unsigned int IER;.

Another issue, again with such a file included from the BSP, is inline assembly asm() sections such as in #define FOO_ASM asm("FOO").

These both of course throws an error when building tests as GCC does not recognise these keywords.

I had thought I could prevent these BSP headers from being added in by having Ceedling generate a mock, by adding #include "mock_a.h" to my test file, but GCC still compiles a.h, and thus b.h.

Is there a best practice way of solving such issues?

I could add something like the below to the BSP file in question, but I am loath to alter vendor code that would change or overwrite my changes with a new version release, I would rather understand how to isolate the unit properly.

// Unknown how __cregister is initially defined
#ifdef TEST
    #undef __cregister // Redefine __cregister to nothing
    #define __cregister
#endif

extern __cregister volatile unsigned int IER;
Toby
  • 9,696
  • 16
  • 68
  • 132
  • Im unsure, but it looks like this [blog section](https://dmitryfrank.com/articles/unit_testing_embedded_c_applications#dealing_with_compiler-specific_stuff) might help - would welcome comments before I attempt it for a mass of BSP files... – Toby Jan 17 '17 at 12:09
  • Obviously you can't test inline assembler on a system which is not the target system. – Lundin Jan 17 '17 at 12:28
  • @Lundin Obvs. But the question is, what is the method of isolating the unit that includes a file that inlines asm? – Toby Jan 17 '17 at 12:47
  • This would be covered by proper program design. You won't be able to test any drivers or hardware-related programming outside the designated system, so you would only interact with the HAL. The drivers would have to be simulated. Quite a lot of work. Which is why testing such things outside the designated hardware is a big waste of time. – Lundin Jan 17 '17 at 13:06
  • It also requires space on the device for the test and output source as well as the unit source - and knowledge of the good practices in unit testing for embedded C, something I am currently lacking in a bit, hence my question :-) – Toby Jan 17 '17 at 13:14
  • @Lundin, if you have any link to a comprehensive guide/how-to on embedded C testing (I find unit framework documentation to not be the best way of learning it), then it would also be a welcome comment. I am part way into James grenning's book but have yet to move to target based tests – Toby Jan 17 '17 at 13:17
  • 1
    The whole TDD hype is actually quite good for this, as long as you look at the program design aspects. In order to write code that is testable, the programmer is forced to write HALs and encapsulate drivers. This was of course always how to properly design embedded C programs. So if the program is properly designed, how to test should follow intuitively. – Lundin Jan 17 '17 at 14:37
  • 1
    As it turns out, the best way to test a certain project is highly project-specific. There is no golden rule for how to best test "a generic piece of code" - that's just nonsense. Instead, the tests should come intuitively from the program design, which in turn comes from the requirement specification. Ideally, for each set of requirements there should be a program module, and for every such module there should be a test, based on the requirements. And that's about it. Everything else is fluff and buzzwords. – Lundin Jan 17 '17 at 14:38

1 Answers1

1

It ended up that I followed the method outlined in the link in my comment to the OP.

So for the example from my original post

/* foo.h */
extern __cregister volatile unsigned int IER;
#define FOO_BAR_ASM asm("BAR");

I created the following file, with the same name, within the test/support/ directory and removed the include path for the real BSP files from the test build settings:

/*foo.h - in test/support */
extern volatile unsigned int IER;
#define FOO_BAR_ASM

Then added an include like #include "mock_foo.h" to the test file.

Toby
  • 9,696
  • 16
  • 68
  • 132