Giter Site home page Giter Site logo

Comments (6)

mvandervoord avatar mvandervoord commented on June 15, 2024

You've done a great job of documenting the issue. I really appreciate that. I don't have the time at the moment to run your example directly, but I have a guess as to your error, and I have a philosophy of how best to handle these situations.

Philosophy:

The root problem you're running into is a common one: one where headers aren't really "standalone" without depending on other headers for information... this is particularly common in complex API's.

If the API is complex enough, the easiest thing to do is to create your own header files that has just the types and functions that you care about. You can then use that instead of directly calling the original headers. Let's say we made such a header timer_api.h:

#ifndef TEST
// When we're not testing, just include the original headers
#include "dl_timerg.h"
#include "dl_timer.h"
#else
// we can still include headers to grab types.
// we put function prototypes here to be mocked.
// we can even put function prototypes here for things that are macros in the real API!
#endif

Now, in both your release code and your test code, include timer_api.h instead of using the individual timer files. It's easily mockable (a win for tests) and it serves as documentation for what parts of the complex API you are actually using (good for maintenance). It also protects you against API changes if those header files are upgraded or settings are changed. If the API changes below, you now potentially have the option to update the abstraction in one place (this wrapper file) instead of everywhere it is called.

The Error Ceedling is Giving You:

It is possible that the error you're seeing is exactly what you've said... a mismatch in what is getting used in two different places. Most often, though, these mismatches will fail to compile or link completely. It's rarely a runtime error.

Is DL_TimerG_isPowerEnabled a macro that directly passes through to DL_Timer_isPowerEnabled? If so, I'm not seeing how this could cause a crash. You're pulling in the mock of both headers, so it's going to create a fake version of DL_Timer_isPowerEnabled... The macro DL_TimerG_isPowerEnabled remains unchanged, so it will still want to call DL_Timer_isPowerEnabled. When linked, the DL_Timer_isPowerEnabled it is connected to is the one that has been mocked, because otherwise there would be a conflict of multiple definitions. Your expectation would be the correct one and everything should pass.

My suspicion would be that DL_Timer_isPowerEnabled does more than just pass the call along to the next step, and perhaps that's what's failing. I'd be doubly suspicious of anything that uses a pointer (these API's often like to pass around handles, etc). There's a good chance that a handle is being dereferenced or something when it has never been initialized.

In any case, best of luck. I hope this helps.

from ceedling.

DanieleNardi avatar DanieleNardi commented on June 15, 2024

Thank you very much for the suggestion!

To answer your question, yes, DL_TimerG_isPowerEnabled is a macro to DL_Timer_isPowerEnabled, which is static inline. Is there a way in project.yaml to tell the linker which file to get?

from ceedling.

mvandervoord avatar mvandervoord commented on June 15, 2024

Ceedling automatically knows which file to use based on your test. You included the mocked header, so it knows to use the mocked C file for the test, not the normal C file. If you included both the mock and unmocked header, it would attempt to pull in both and it would fail.

I think we found the problem, though! DL_Timer_isPowerEnabled is static inline! So even though you're properly mocking this interface, the func call has already flattened the static inline to the things it actually does and doesn't believe there is anything to call there (Oh C... you're painful sometimes!).

Are you able to modify either of these files? If not, your only option is the long API explanation I wrote above. If you CAN tweak these files slightly, maybe you want to change inline to INLINE. Then use this:

#ifdef TEST
#define INLINE
#else 
#define INLINE inline
#endif

This will make it so that the function isn't inlined during tests and you can mock it properly.

from ceedling.

DanieleNardi avatar DanieleNardi commented on June 15, 2024

Ceedling automatically knows which file to use based on your test. You included the mocked header, so it knows to use the mocked C file for the test, not the normal C file. If you included both the mock and unmocked header, it would attempt to pull in both and it would fail.

I think we found the problem, though! DL_Timer_isPowerEnabled is static inline! So even though you're properly mocking this interface, the func call has already flattened the static inline to the things it actually does and doesn't believe there is anything to call there (Oh C... you're painful sometimes!).

Are you able to modify either of these files? If not, your only option is the long API explanation I wrote above. If you CAN tweak these files slightly, maybe you want to change inline to INLINE. Then use this:

#ifdef TEST
#define INLINE
#else 
#define INLINE inline
#endif

This will make it so that the function isn't inlined during tests and you can mock it properly.

This solution works, but now a new issue has entered: I get some assembler errors, die to the fact that the CMSIS included in the SDK adds some __ASM which is not recognized by the native GCC, so now I have ti find a way to workaround the cmsis_gcc.h. I guess the proper way is to compile with ARM GCC and run unit test directky on chip or a simulated environment (QEMU?). Do you have any suggestion?

from ceedling.

mvandervoord avatar mvandervoord commented on June 15, 2024

Yes. You can run on QEMU and it'll solve this particular issue. It'll also have the benefit of testing using the same compiler your release uses, etc.

The alternative is still to build an API file as I described above, which avoids the entire CMSIS layer altogether.

Either will work. They have pros and cons and it's really a matter of preference.

from ceedling.

DanieleNardi avatar DanieleNardi commented on June 15, 2024

Yes. You can run on QEMU and it'll solve this particular issue. It'll also have the benefit of testing using the same compiler your release uses, etc.

The alternative is still to build an API file as I described above, which avoids the entire CMSIS layer altogether.

Either will work. They have pros and cons and it's really a matter of preference.

Thank you very much!

from ceedling.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.