ThrowTheSwitch / CMock

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

Named nested parameters creates syntactically incorrect mocks #51

Open ghost opened 9 years ago

ghost commented 9 years ago

Mocking the following function:

void foo(void __stdcall * bar(int arg0));

creates the mock:

void foo(void __stdcall* bar(int arg0))
{
  UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;
  CMOCK_foo_CALL_INSTANCE* cmock_call_instance = (CMOCK_foo_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.foo_CallInstance);
  Mock.foo_CallInstance = CMock_Guts_MemNext(Mock.foo_CallInstance);
  UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, "Function 'foo' called more times than expected.");
  cmock_line = cmock_call_instance->LineNumber;
  {
    UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(&cmock_call_instance->Expected_arg0)), (void*)(&arg0)), sizeof(void __stdcall* bar(int), cmock_line, "Function 'foo' called with unexpected value for argument 'arg0)'.");
  }
}

The line

UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(&cmock_call_instance->Expected_arg0)), (void*)(&arg0)), sizeof(void __stdcall* bar(int), cmock_line, "Function 'foo' called with unexpected value for argument 'arg0)'.");

Seems to have a misplaced paren, as the open/close match ends at UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(&cmock_call_instance->Expected_arg0)).

mvandervoord commented 9 years ago

:) You've found a lot of interesting corner cases today. I'll be sure to figure out how to handle these.

Out of curiosity, what codebase is it that has these odd cases? In the 10 years this project has been live, you're the first person to bring an example that typdefs a void (which isn't even ANSI C), then uses it as a void pointer.

Don't get me wrong, I still intend to fix these bugs in CMock. Our goal is to have it handle any code you can throw at it... I'm just curious where you are finding these fascinating cases! :)

Thanks for bringing them to our attention! I'm on it!

Mark

ghost commented 9 years ago

These are some oddities from the Windows API - for example, in winuser.h the function LoadMenuIndirectA() uses a type defined as void:

typedef VOID MENUTEMPLATEA;
typedef VOID MENUTEMPLATEW;
WINUSERAPI
HMENU
WINAPI
LoadMenuIndirectA(
    __in CONST MENUTEMPLATEA *lpMenuTemplate);

So I guess we can blame Microsoft : )

  1. https://msdn.microsoft.com/en-us/library/windows/desktop/ms647991(v=vs.85).aspx
mvandervoord commented 9 years ago

Hahahah... well, that's always a good strategy. :)

In any case, thanks for posting these! I'll do my best to get them fixed in a timely manner.

Mark

ghost commented 9 years ago

Maintaining a unit testing framework has to be such a thankless task - thank you so very much for your dedication! CMock really is a useful framework.

mvandervoord commented 9 years ago

Thanks, I appreciate that. :)

I alternate between feeling very proud of how far it has come, and frustrated with how far it still has to go before it "just works."

ghost commented 9 years ago

edit: I meant to start this 'truly good products are never really finished' but I got a little hasty.

What parser do you use to parse function definitions? I can envision a function that takes a function as a parameter that takes a function as a parameter... that returns a function that takes a function as a parameter...

Surely there's no regex that can accomplish this, and while any sane person would use a series of typedefs, there's always that one...

edit2: if you want I can move this question into a forum post

mvandervoord commented 9 years ago

Nah, it's fine here.

We are currently parsing using a series of regular expressions and homemade parsers. If you ever decide to build something like this, I would recommend NOT doing it that way. It seems like a good idea to reduce dependencies on other tools... but I assure you that you will always be playing catch-up with a "real parser". ;)

We decided to go this way originally because (1) to reduce dependencies and (2) we could then cover all sorts of non-standard C features that we encounter with weird embedded C compilers. There are times that #2 has paid off... but I think overall it would have been better to start with a full parser as a foundation.

We're actually starting a new version of CMock that is almost from the ground up... it very likely is going to have a full C parser engine built in.

Mark

ghost commented 9 years ago

I look forward to seeing such a parser - what do you plan on writing it in (continue with ruby / migrate to python) ? Also, would you plan on making the parser standalone and integrating it into the framework? The reason I ask is some things like Doxygen actually have a parser implemented, but don't expose their functionality directly.

mvandervoord commented 9 years ago

Sorry, I don't think I was clear there. The version of CMock is going to be a rewrite. I think the parser is likely going to be based on an existing parser out there (possibly modified to support more embedded quirks).

As for language and whatnot, that's a bit up in the air. If it's a scripting language, it'll likely still be Ruby. The main developers all seem to prefer Ruby over its close scripting cousins. Otherwise maybe a compiled language for better speed?

barneywilliams commented 9 years ago

Ultimately, it would be nice for everyone if CMock was all C, but doing all of this work in C would take a lot more work than using a scripting language.

I recently added preliminary support for Make build integration of CMock, in lieu of the full-on Ruby/Rake approach we tried and still regret in the case of Ceedling ;)

We actually did try and use a true grammar parser at one point, but it ended up adding MUCH more overhead and was too painful to use, without that much added benefit.

We have been discussing using some more low-level parsers to make it more useable... Just need to find the time for such a large task ;)