ThrowTheSwitch / CMock

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

[BUG] Cmock functions are generated incorectlly in some cases #467

Open dkDeman opened 11 months ago

dkDeman commented 11 months ago

Sometimes developer use the macros to include/exclude the functions in the source code, like as:

gpio.h

#ifndef _GPIO_H_
#define _GPIO_H_

void gpio_init(void);

#if defined(GPIO_BLA_BLA)
    static inline void gpio_test(void) {}
#else
    #define gpio_test
#endif

#endif

If GPIO_BLA_BLA is defined then gpio_test function should be used in the source code. If GPIO_BLA_BLA is NOT defined then gpio_test function should NOT be used in the source code.

The cmock functions are generated for all types in the header file regardless of macros. If we run unit test for modules which uses gpio driver, then cmock function is generated for gpio_testfunction even if GPIO_BLA_BLAis not defined.

mock_gpio.h:

#ifndef _MOCK_GPIO_H
#define _MOCK_GPIO_H

#include "unity.h"
#include "gpio.h"

//cmock functions

#endif

mock_gpio.c:

#include "mock_gpio.h"

#if defined (__IAR_SYSTEMS_ICC__)
#pragma weak gpio_test
#else
void gpio_test(void) __attribute ((weak));
#endif

void gpio_test(void)
{
   //code
}

Let's try to build it. Since GPIO_BLA_BLAis not defined then compiler replace the gpio_test string to the empty place (please see gpio.h file), as result we will get the following error:

image

If we comment out the "#define gpio_test" line in the gpio.h file than it is working.

gpio.h

#ifndef _GPIO_H_
#define _GPIO_H_

void gpio_init(void);

#if defined(GPIO_BLA_BLA)
    static inline void gpio_test(void) {}
#else
    //#define gpio_test
#endif

#endif

To fix it, CMock should add the GPIO_BLA_BLA to the mock_gpio.c: file, like as:

#include "mock_gpio.h"

#if defined (GPIO_BLA_BLA )
void gpio_test(void)
{
   //code
}
#endif

Or, CMock should not generate the cmock functions for gpio_testfunction if GPIO_BLA_BLAis not defined.

All source files: src.zip

Letme commented 11 months ago

Its not a bug, its bad coding from your side.

Rather then using ifdef-else clause use define for WEAK and then once its defined __attribute (( weak ))__ and other time as weak or in unit test case as empty. You will have to add some strippables in this case (you could do it in yourcase as well).

Secondly, you are mixing inline implementation of the functions with declaration. I would propose that you split declarations (in gpio.h) and then put gpio_inline_impl.h for implementation/definition. Then you can conditionally include gpio_inline_impl.h with #ifndef TEST to enable the inline functions when not running in test mode and to get mocks.

dkDeman commented 11 months ago

Maybe you are rigth, but the sample has been built on basic the zephyr spi.h file for spi_transceive_stats function.

#if defined(CONFIG_SPI_STATS)

//code

static inline void spi_transceive_stats(const struct device *dev, int error,
                    const struct spi_buf_set *tx_bufs,
                    const struct spi_buf_set *rx_bufs)
{
    // code
}

#else /*CONFIG_SPI_STATS*/

#define spi_transceive_stats(dev, error, tx_bufs, rx_bufs)

#endif /*CONFIG_SPI_STATS*/

We can't change the zephyr source code.

mvandervoord commented 8 months ago

If you're using Ceedling, you can enable preprocessing to preprocess your files before they get mocked. This solves most of these kinds of issues.

The root problem, that CMock doesn't fully parse C code with full preprocessing capabilities, etc, is a known issue and is one we mean to address. I apologize that it's still an issue after this long.