ThrowTheSwitch / CMock

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

CMock cannot generator mocks for function which uses incomplete struct type. #85

Closed a-d-v-e-n-t-u-r-o-u-s closed 7 years ago

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

Hello,

I've downloaded the most recent version of CMock from the origin/master branch with SHA-1 f341e3f650ffa2b301768b5a1acbea0264999db2 and then I've tried to play with it a little bit. I've encountered a problem when I was trying to generate mocks for function which argument is of incomplete struct type, please find my modified foo.c, foo.h and test_foo.c files:

[https://gist.github.com/a-d-v-e-n-t-u-r-o-u-s/1089367da8f2ba7af39caf900400c062]

Here is also console output:

build/test/mocks/mock_foo.c: In function ‘foo_attach’:
build/test/mocks/mock_foo.c:140:107: error: invalid application of ‘sizeof’ to incomplete type ‘struct FOO_handle_t’
     UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(cmock_call_instance->Expected_handle), (void*)(handle), sizeof(struct FOO_handle_t), cmock_line, CMockStringMismatch);
                                                                                                           ^
/home/babula/Documents/source/github/cmock/vendor/unity/src/unity_internals.h:670:191: note: in definition of macro ‘UNITY_TEST_ASSERT_EQUAL_MEMORY’
 ITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message)                     UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(len), 1, (m
                                                                                                                                                                                     ^
build/test/MakefileTestSupport:59: recipe for target 'build/test/mocks/mock_foo.o' failed

Can you please advise is there some magic switch in the order to turn off those error messages ? Currently the simplest solution would be moving structure definition to the header file.

mvandervoord commented 8 years ago

Hi.

Yes, CMock can handle this. When you don't give CMock enough information about a type, it attempts to do a memory comparison as a fallback plan. When your compiler doesn't even have enough information at this point (as is your situation... you're not linking to a file that actually understands what that struct type is), it will result in a failure.

There are two decent work-arounds for this kind of issue.

(1) You can add a unity helper and create a custom handler for that new type. You can refer to this old article which has not yet been ported to the new website: http://throwtheswitch.squarespace.com/white-papers/cmock-and-custom-types.html

(2) You can switch to pointer comparisons instead of memory comparisons. Instead of :when_ptr: being set to :smart (which is the default), you can specify that you want it set to :compare_ptr. At that point it will do a POINTER comparison instead of a memory comparison. Keep in mind that in the current version of CMock this is a universal setting for that entire compiled mock.

Good luck!

Mark

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

Hi Mark,

At the beginning I've followed the workaround (2) and it worked perfectly. The previous macro :

UNITY_TEST_ASSERT_EQUAL_MEMORY

was replaced by following macro, which didn't try to use sizeof of my incomplete structure:

UNITY_TEST_ASSERT_EQUAL_PTR

However I couldn't manage with workaround (1). I've setup my config.yml back to the :smart setting and I've created custom unity helper and have given config.yml path to it but still without success. Here is fragment of my UnityHelper.h header file , which I've created:

void AssertEqualFOO_handle_t(const struct FOO_handle_t expected, const struct FOO_handle_t actual, const unsigned short line);

#define UNITY_TEST_ASSERT_EQUAL_FOO_handle_t(expected, actual, line, message) AssertEqualFOO_handle_t(expected, actual, line);

#define TEST_ASSERT_EQUAL_FOO_handle_t(expected, actual) UNITY_TEST_ASSERT_EQUAL_FOO_handle_t(expected, actual, __LINE__, NULL);
mvandervoord commented 8 years ago

When you say "given config.yml path to it" does that mean you have added it to the build path or added the following?

:cmock:
    :unity_helper_path: "blah/UnityHelper.h"

Yes, it's looking for the header file. That way, it can look at your macros and decide if any of them fit the template used internally by CMock (which is the version that starts with UNITY_).

If you're not doing both of these things, you're going to want to. If you are already, can you send me the mocked file and the original header? I'd like to see if there is something missing.

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

When you say "given config.yml path to it" does that mean you have added it to the build path or added the following?

:cmock: :unity_helper_path: "blah/UnityHelper.h"

Yes, exactly.

Yes, it's looking for the header file. That way, it can look at your macros and decide if any of them fit the template used internally by CMock (which is the version that starts with UNITY_).

If you're not doing both of these things, you're going to want to. If you are already, can you send me the mocked file and the original header? I'd like to see if there is something missing.

What I'm actually trying to do is to build in cmock and unity in the build system on which I'm currently working on. Here you have some test branch where I've pushed what I've done till now.

https://github.com/a-d-v-e-n-t-u-r-o-u-s/WeatherStation/tree/test

Please clone the repository and try to type make test then generating of mocks shall be started. I'm not done yet with some clean target, so clean is some kind of manual or through git clean -xdf. The header file with which I've problems is /drivers/pwm/include and it's mock shall be located in at /mocks/pwm/include and /mocks/pwm/source

mvandervoord commented 8 years ago

Thanks. :)

Instead of having to debug the build for my entire platform (OS X), is there a quick way to have it just execute cmock the way you are building? If not, I'll have to wait until I have time to dig in and update the makefiles.

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

I'll just send you then what I get :) Here you go: https://gist.github.com/a-d-v-e-n-t-u-r-o-u-s/74110d0f0b267876fd30028678d0d2f3

and here header file from which I've tried to generate the mock: https://gist.github.com/a-d-v-e-n-t-u-r-o-u-s/9a6612ae83b69b80f24a1ba28c12cd91

mvandervoord commented 8 years ago

When you execute the makefile, you are in the root directory, right? So I think your config file backs up a couple of steps when it doesn't need to?

Maybe try :unity_helper: "framework/unityhelper/include/UnityHelper.h"

Also, I don't think this is the problem, but I notice that you have both unity_helper and UnityHelper. I don't think you're going to want both.

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

When you execute the makefile, you are in the root directory, right? So I think your config file backs up a couple of steps when it doesn't need to? Maybe try :unity_helper: "framework/unityhelper/include/UnityHelper.h"

Actually I'm not. When I call command for generating the mock I walk into directory where this mock shall be stored e.g. /mocks/common, /mocks/pwm . When I change location of the helpers I'm getting following error message:

ruby /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock.rb -o/home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/config.yml  /home/user_name/Documents/source/github/WeatherStation/drivers/common/include/common.h
/home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock_config.rb:87:in `initialize': No such file or directory @ rb_sysopen - framework/unityhelper/include/UnityHelper.h (Errno::ENOENT)
from /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock_config.rb:87:in `new'
from /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock_config.rb:87:in `load_unity_helper'
from /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock_unityhelper_parser.rb:49:in `import_source'
from /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock_unityhelper_parser.rb:14:in `initialize'
from /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock.rb:23:in `new'
from /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock.rb:23:in `initialize'
from /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock.rb:88:in `new'
from /home/user_name/Documents/source/github/WeatherStation/framework/cmockgenerator/lib/cmock.rb:88:in `<main>'

Also, I don't think this is the problem, but I notice that you have both unity_helper and UnityHelper. I don't think you're going to want both.

Yes I have, because at the beginning I wanted to name helper files in similar manner as all unity files, but then I've seen that in documentation you used name UnityHelper.h and I followed the white papers. In the end I've seen the option where you are directing Unity to take some custom helper but didn't delete the old files. Please remember this is draft ;) after deleting those files nothing has changed.

mvandervoord commented 7 years ago

Darn. I let that conversation lapse a long time. Did you ever figure out what the issue was? It was really sounding like it was going to be a build-system specific issue and not core CMock. I'm going to close this issue. Please feel free to reopen it if you feel I am wrong. As for making builds easier: That's a big part of what I am focusing on now!

MaartenMJR commented 3 years ago

@mvandervoord I came up with a similar problem as reported here, in a situation where several files can implement an interface. I've managed to work around the sizeof problem by a simple work-around in the unity_helper:

* @file     ceedling_mock_helper.h
* @brief    (Type) definitions needed for compilation of ceedling mocks
* @author   
*******************************************************************************/

#ifndef CEEDLING_MOCK_HELPER_H
#define CEEDLING_MOCK_HELPER_H

#if defined(_MOCK_GENERICBUSINTEGRATION_H) || defined(_MOCK_GENERICBUSINTEGRATION_MESSAGE_H)
/*
 * Ceedling mocking frame work is not able to deal with incomplete Message_t
 *           and PheripheralDevice_t structures.
*/
struct Message_t {
    uint8_t __raw_data[100];
};

struct PeripheralDevice_t {
    uint8_t __raw_data[100];
};
#endif

#endif

So effectively when the mocks are generated that use the incomplete types in their mocked function interfaces, the compiler will use an empty struct for the sizeof operation. Of course the memory comparison utilities are not to be used in combination with these mocks.

Where unity_helper is defined as _ceedling_mockhelper.h in the project.yml file like so:

  :mock_prefix: mock_
  :when_no_prototypes: :warn
  :enforce_strict_ordering: :TRUE
  :plugins:
    - :ignore
    - :callback
    - :expect_any_args
    - :ignore_arg
    - :array
    - :return_thru_ptr
  :verbosity: 3
  :when_ptr: :compare_data
  :unity_helper:
  - Inc/ceedling_mock_helper.h