ThrowTheSwitch / CMock

CMock - Mock/stub generator for C
http://throwtheswitch.org
MIT License
682 stars 276 forks source link

Mocking static inline functions #150

Closed laurensmiers closed 5 years ago

laurensmiers commented 7 years ago

Hi,

This is more a question than an issue, but maybe CMock has a solution for this that I don't know yet. I found this issue #103, but this only covers 'static' functions not 'static inline' as far as I can understand.

I was wondering what the best strategy is to mock "static inline" functions using CMock (or if there is a way to do this?).

Some context: I'm writing an application on a Kinetis Freescale board and using Ceedling/Cmock to write my tests/generate mocks. I am using it quite successfully but now I am trying to mock a driver from the Kinetis SDK. The problem was that CMock was not generating mocks because the functions were all of the following signature:

static inline void myFunc(<some params>) 
{
 //some implementation
}

I tweaked CMock to have it make mocks for this kind of function as well but I forgot that the compiler/linker will now have 2 definitions in the mocked header file (since CMock includes the original header file in the mocked header file).... This gives the following linking error when trying to compile the mock:

build/test/mocks/mock_fsl_port.c:178:6: error: redefinition of ‘PORT_SetPinConfig’ void PORT_SetPinConfig(PORT_Type base, uint32_t pin, const port_pin_config_t config) ^~~~~ In file included from build/test/mocks/mock_fsl_port.h:9:0, from build/test/mocks/mock_fsl_port.c:7: ../ksdk/SDK_2.2.1_MKL27Z256xxx4/devices/MKL27Z4/drivers/fsl_port.h:256:20: note: previous definition of ‘PORT_SetPinConfig’ was here static inline void PORT_SetPinConfig(PORT_Type base, uint32_t pin, const port_pin_config_t config) ^~~~~

I circumvented the issue for now by copying the SDK header file to a mocked SDK folder (and setting it as the first include path before the actual SDK when compiling the mocks/tests). I also removed the implementation of the static inline functions (and also removing the static inline to have them recognized by CMock as 'normal' functions). So f.e. static inline void PORT_SetPinConfig(PORT_Type *base, uint32_t pin, const port_pin_config_t *config) {...} becomes void PORT_SetPinConfig(PORT_Type *base, uint32_t pin, const port_pin_config_t *config); in the new mock port.h header file That way I can have _Expect, _Ignore, etc. for these SDK functions (I just want mocks for it that I can use for the tests).

I don't want to 'adapt'/touch the ksdk driver code to the tests, so I am wondering what's the best strategy to tackle this sort of functions? Does CMock provide something to do this or is the way described above the way to go or something else...?

Thanks for reading the wall of text if you got this far, KR,

Laurens

SteinHeselmans commented 7 years ago

I also struggled with those static inline functions, and haven't really found a good solution.

For now, i am doing

#if !defined(UNITTEST) /* for unit test, mocks will be generated */                                                     
static incline void foo(void) {
    // act like foo
}
#endif

and the UNITTEST is added to the -D options of gcc only when building for unit test.

The drawback: we can't unit test this actual function, and even if we're testing the component where this function resides we use a mock function...

So, I'm also curious about other solutions?

laurensmiers commented 7 years ago

@SteinHeselmans thanks for your suggestion. I was doing the same as you but then I bumped into static inline functions in third-party library stuff that I would like to leave untouched (so altering them is not an option...) . That's why I 'mocked' the headers containing these static inline functions but it also doesn't feel like a solid solution...

paulsc commented 6 years ago

I have the same problem. I can fix it by creating a new header file with no inline functions, but it's a tedious process so I was wondering if there is a config option for this ? Maybe something like the opposite of ":strippables" where you can explicitly add functions to be mocked ?

kromancer commented 6 years ago

Yeap, I will agree with my fellow raccoons, mocking static inline functions would be a nice feature. I am now working with nordic's NRF5 SDK which is full of those.

lafumei commented 6 years ago

Hi, +1 from me as well, I feel this would be an awesome feature. I'm working with STM32 dev tool libraries which contain loads of static inliners - and I imagine a lot of SDKs will have these too. The only practical solution I found was to manually mock the header files which is a long tedious process.

Looking forward to having ceedling do this for me! 💯

czee commented 6 years ago

+1 Yes, this would be a good feature

a-d-v-e-n-t-u-r-o-u-s commented 6 years ago

Hi, I've also encountered problem with static inline functions. It would be definitely something nice to have.

Miv13 commented 6 years ago

+1 from me as well, I'd also greatly appreciate some other ideas for a 'more automatic' solution.

laurensmiers commented 6 years ago

Some initial thoughts, just to start the discussion:

So basically, stripping "static inline" and the implementation of the static-inline functions from the original header before starting the parsing. This should make cmock handle the static-inline functions as a normal function I guess?

However, if we don't include the original header, we have to make sure no defines/macros/... are lost. So these would have to be copied to the mock-header as well.

kevlut commented 5 years ago

+1 from me on this front. Biggest issue is with vendor code.

bigbrett commented 5 years ago

+1 literally every single SDK driver I use from NXP and Nordic have these. Many from TI do as well. Please please PLEASE let us mock these, I can't think of a good reason why you wouldn't?

alexiabrazeau commented 2 years ago

+1 from me too. I'm having issues with it too! I tried creating a new header file and putting the function prototype in it, but then I get errors because of multiple declarations (because another file uses the same function)...

Letme commented 2 years ago

@alexiabrazeau there is already a solution merged into latest cmock - does that work for you?

alexiabrazeau commented 2 years ago

@Letme Hi, my issue is that I am trying to mock a function that doesn't have a prototype. It's the first time I see this, the function is declared in the header file like so :

__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{
  NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */
}

I am really confused as to how I'll mock this function to be honest, it's a very specific case

mot0ko commented 2 years ago

I know this is old, but the method I'm using to mock static inline function is based on some trivia:

So what I usually do is copy the content of the original header file into the new one and mock everything in it (or not depending on the need) and then I include the unit tests mocking folder before the other ones (as the first entry for example) this way the mocked header is the one taken instead of the real one from the library. Works like a charm and it allows your original source files to be free of unit test conditional compilations (which means you don't need to modify library files)

Problem with it is that you usually have to copy the whole header file and mock some other stuff, when the library file changes, you have to update yours as well, nothing extraordinary but still some maintenance.

btw it's basically the same idea as when you wanna override the default malloc implementation for example, you just snatch the request of the dynamic library linking at runtime to redirect it, well it's the same idea here but with the header files search