ThrowTheSwitch / CMock

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

[Bug] "Failed Parsing Declaration Prototype!" for different macros #466

Open dkDeman opened 10 months ago

dkDeman commented 10 months ago

Very often, if the header file contains many different macros, then when running the test the following error occurs:

image

For example, let's look the unit tests for the DCDC driver which uses the GPIO driver with different macros.

gpio.h file:

#ifndef _GPIO_H_
#define _GPIO_H_

#define GPIO_SECT_DECL(group)   \
    struct gpio_ ## group       \
    {                           \
        int a;                  \
    }

#define GPIO_SECT_END

GPIO_SECT_DECL(test)
GPIO_SECT_END;

void gpio_init(void);

#endif

dcdc.h file:

#ifndef _DCDC_H_
#define _DCDC_H_

void dcdc_init(void);

#endif

dcdc.c file:

#include "gpio.h"
#include "dcdc.h"

void dcdc_init() {
    gpio_init();
}

Unit tests:

#include <unity.h>

#include "dcdc.h"
#include "driver/mock_gpio.h"

void setUp(void)
{
}

void tearDown(void)
{
}

void test_dcdc_init_should_do_nothing_when_called(void)
{
    gpio_init_Expect();
    dcdc_init();
}

void main(void)
{
    unity_main();
}

If we run units for the driver then we will get a successful result:

image

However, if we do one small change in the gpio.h file like as:

#ifndef _GPIO_H_
#define _GPIO_H_

#define GPIO_SECT_DECL(group)   \
    struct gpio_ ## group       \
    {                           \
        int a;                  \
    }

GPIO_SECT_DECL(test);

void gpio_init(void);

#endif

or like as:

#ifndef _GPIO_H_
#define _GPIO_H_

#define GPIO_SECT_DECL(group)   \
    struct gpio_ ## group       \
    {                           \
        int a;                  \
    };

GPIO_SECT_DECL(test)

void gpio_init(void);

#endif

then we will get "Failed Parsing Declaration Prototype"

image

Also, we will get this error if empty macros is used in gpio.h file, for example:

#ifndef _GPIO_H_
#define _GPIO_H_

#define GPIO_SECT_DECL(group)

GPIO_SECT_DECL(test)

void gpio_init(void);

#endif
Letme commented 10 months ago

For what you want, I would preprocess the header files before including them into CMock. Might be a better solution, because CMock uses regexp parser and not a proper C preprocessor, so it is not aware of such intricate concatenations.

I doubt there is an easy way to adjust regexp to catch this cases.

dkDeman commented 10 months ago

It is Ok if external code is not used. However, we use zephyr RTOS in our projects. And we have this error for zephy header files. For example, for spi.h. Of couse, we can change the our source code, but we can't change the zephyr source code.

If you look stats.h. then you can detect that zephyr use empty macros depending on configuration, for example:

#ifdef CONFIG_STATS

/**
 * @brief Begins a stats group struct definition.
 *
 * @param group__               The stats group struct name.
 */
#define STATS_SECT_START(group__)  \
    STATS_SECT_DECL(group__) { \
        struct stats_hdr s_hdr;

/**
 * @brief Declares a 32-bit stat entry inside a group struct.
 *
 * @param var__                 The name to assign to the entry.
 */
#define STATS_SECT_ENTRY(var__) uint32_t var__;

/**
 * @brief Declares a 16-bit stat entry inside a group struct.
 *
 * @param var__                 The name to assign to the entry.
 */
#define STATS_SECT_ENTRY16(var__) uint16_t var__;

/**
 * @brief Declares a 32-bit stat entry inside a group struct.
 *
 * @param var__                 The name to assign to the entry.
 */
#define STATS_SECT_ENTRY32(var__) uint32_t var__;

/**
 * @brief Declares a 64-bit stat entry inside a group struct.
 *
 * @param var__                 The name to assign to the entry.
 */
#define STATS_SECT_ENTRY64(var__) uint64_t var__;

#else /* CONFIG_STATS */

#define STATS_SECT_START(group__) \
    STATS_SECT_DECL(group__) {

#define STATS_SECT_ENTRY(var__)
#define STATS_SECT_ENTRY16(var__)
#define STATS_SECT_ENTRY32(var__)
#define STATS_SECT_ENTRY64(var__)

#endif