ThrowTheSwitch / CMock

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

RFC: Option to throw C++ exception instead of using longjmp #387

Closed 0xjakob closed 3 months ago

0xjakob commented 2 years ago

Hello, we are using CMock for some host-based unit tests in ESP-IDF. It is the best, most extensible and stable test framework we found so far. We plan to extend it's usage to most of our tests. However, we have two special circumstances:

  1. We mostly use CATCH for our host-based tests. Even though we also have a lot of C code, CATCH turns out to be easier to use,
  2. We eventually use a lot of C++ code, too, which calls mocked C code.

Regarding 2: using longjmp is a problem since it basically creates undefined behavior once C++ objects with non-trivial destructors have been allocated in the stack area over which longjmp jumps. In practice, this means that once any failure from CMock has been detected in our C++ tests, we need to restart the entire test application.

Regarding 1: CATCH also uses C++ exceptions, as far as I could investigate. Currently, provide CMock compatibility via some glue code which makes sure that "translates" failed setjmp calls into exceptions. E.g., we use a C++ struct as fixture to do the "translation". But this is a nasty hack and only yield in a generic error message. Furthermore, CATCH catches C++ exceptions and prints out their error messages from the what() method automatically, once they reach the test function.

Possible solution:

It would be nice to have an option when using CMock to switch to throwing C++ exceptions instead of calling longjmp. CMock usually prints out the error information before calling longjmp. When using C++ exceptions, this information could be transported in the exception's message (or optionally also printed, etc.).

I created a simple PoC a while ago which did the aforementioned (and it worked, too). But obviously, we don't want to maintain an entirely different fork of CMock for IDF. Hence, the question is: Would this option be something the CMock community could accept in the future? We would be very grateful for feedback on this matter, thanks.

Besides, I currently don't how much Unity was involved into this and if/how much I changed Unity for the PoC.

Letme commented 2 years ago

You can always open a Pull Request with the changes that are required and if it does not break existing behavior I am quite sure it will get merged. Keep in mind that cmock works with plugin options and c-exception library (look into vendor folder for https://github.com/throwtheswitch/cexception/tree/71b47be7c950f1bf5f7e5303779fa99a16224bb6), so it might be an option to use cpp-exception library if you cannot fit your changes into the existing cexception one. Since longjmp is a known issue, any fix will be welcomed.

mvandervoord commented 2 years ago

The root mechanism that you're concerned with isn't in CMock at all. It's in the Unity test framework that is actually handling the pass/fail status of tests, etc. The macros you are looking to replace are UNITY_PROTECT() and UNITY_ABORT(). At the moment, you are either using the setjmp mechanism, or you're using a simple return mechanism (based on if you have UNITY_EXCLUDE_SETJMP_H defined.

We'd definitely consider a submission to the Unity project which, if UNITY_EXCLUDE_SETJMP_H were defined, allowed the user to switch in another option like UNITY_INCLUDE_CPP_EXCEPTION which replaced these macros with a try... catch implementation.

0xjakob commented 2 years ago

@Letme @mvandervoord Thanks, this is enough information for us. If you want to close this ticket for cosmetic reasons, feel free to do so. I currently can't promise that we will implement this, even more I can't promise that we'll do this soon.