ThrowTheSwitch / CMock

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

CMock chokes on volatile function parameters #135

Open austinglaser opened 7 years ago

austinglaser commented 7 years ago

If an interface has functions with volatile non-pointer parameters (i.e. void func(volatile int x)), the code CMock generates issues warnings.

The volatility (like the const-ness) of function parameters is for the most part an implementation detail. In fact, I would argue that it makes little to no sense to mark a function that way. However, it is correct according to the C standard -- and often CMock users aren't in control of the API they're mocking. I'm myself in a situation where this is the case.

A MWE (using Ceedling), for some context:

src/VolatileInterface.h:

#ifndef VOLATILE_INTERFACE_H
#define VOLATILE_INTERFACE_H
void VolatileInterface_OneWay(volatile int x);
void VolatileInterface_AnotherWay(int volatile x); 
#endif

tests/TestSomething.h:

#include "unity.h"
#include "MockVolatileInterface.h"

project.yml:

:project:
    :build_root: build/
    :release_build: FALSE

:paths:
    :test:
        - tests/**
    :source:
        - src/**

Build output:

Test 'test_something.c'
-----------------------
Creating mock for VolatileInterface...
Generating runner for test_something.c...
Compiling test_something_runner.c...
Compiling test_something.c...
Compiling MockVolatileInterface.c...
build/test/mocks/MockVolatileInterface.c: In function ‘CMockExpectParameters_VolatileInterface_OneWay’:
build/test/mocks/MockVolatileInterface.c:90:10: warning: passing argument 1 of ‘memcpy’ discards ‘volatile’ qualifier from pointer target type [-Wdiscarded-qualifiers]
   memcpy(&cmock_call_instance->Expected_x, &x, sizeof(volatile int));
          ^
In file included from build/test/mocks/MockVolatileInterface.c:2:0:
/usr/include/string.h:42:14: note: expected ‘void * restrict’ but argument is of type ‘volatile int *’
 extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
              ^
build/test/mocks/MockVolatileInterface.c:90:44: warning: passing argument 2 of ‘memcpy’ discards ‘volatile’ qualifier from pointer target type [-Wdiscarded-qualifiers]
   memcpy(&cmock_call_instance->Expected_x, &x, sizeof(volatile int));
                                            ^
In file included from build/test/mocks/MockVolatileInterface.c:2:0:
/usr/include/string.h:42:14: note: expected ‘const void * restrict’ but argument is of type ‘volatile int *’
 extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
              ^
build/test/mocks/MockVolatileInterface.c: In function ‘CMockExpectParameters_VolatileInterface_AnotherWay’:
build/test/mocks/MockVolatileInterface.c:148:10: warning: passing argument 1 of ‘memcpy’ discards ‘volatile’ qualifier from pointer target type [-Wdiscarded-qualifiers]
   memcpy(&cmock_call_instance->Expected_x, &x, sizeof(int volatile));
          ^
In file included from build/test/mocks/MockVolatileInterface.c:2:0:
/usr/include/string.h:42:14: note: expected ‘void * restrict’ but argument is of type ‘volatile int *’
 extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
              ^
build/test/mocks/MockVolatileInterface.c:148:44: warning: passing argument 2 of ‘memcpy’ discards ‘volatile’ qualifier from pointer target type [-Wdiscarded-qualifiers]
   memcpy(&cmock_call_instance->Expected_x, &x, sizeof(int volatile));
                                            ^
In file included from build/test/mocks/MockVolatileInterface.c:2:0:
/usr/include/string.h:42:14: note: expected ‘const void * restrict’ but argument is of type ‘volatile int *’
 extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
              ^
Linking test_something.out...
Running test_something.out...

-----------------------
0 Tests 0 Failures 0 Ignored 
OK
mvandervoord commented 7 years ago

Yeah, we'll have to figure out an option for this. It's going to have to be something configurable.

When you state that "However, it is correct according to the C standard", it's a bit misleading. The C99 standard recommends against using volatile as function call parameters and as return values, stating that it is undefined behavior (because it is implementation-specific) and therefore should not be depended upon.

Up until this point, we've chosen to remove the volatiles during tests because the generated code often would fail on most embedded compilers. To make it work, it would require a lot of internal casting (volatile parameter, then cast to non-volatile internal storage, etc).

It should be possible to make everyone happy here, but it's not going to be a small undertaking. Anywhere we are playing with undefined behavior, it's a place where dragons lie. ;)

nfnbass commented 4 years ago

I have just encountered this, and found this thread. Could this be more simply resolved by first parsing the mocked headers and files under test to strip out all mentions of the volatile keyword, as a precursor to the creation of the mock header, and to produce a modified source file to perform tests on?

mvandervoord commented 4 years ago

This is an option in Ceedling. :)

nfnbass commented 4 years ago

This is an option in Ceedling. :)

Thank you! Could you please point me in the direction of what it's known by in the documentation, please?

niziak commented 3 years ago

There is an option:

:cmock:
  :strippables:
    - volatile

but it doesn't work for function parameters.

    Ceedling:: 0.31.0
       CMock:: 2.5.3
       Unity:: 2.5.2
  CException:: 1.3.3

Header file:

void myfn(custom_t *m, volatile uint32_t *var);

It works correctly with CMock when typedef is used:

typedef volatile uint32_t my_volatile_type;
void myfn(custom_t *m, my_volatile_type *var);