PSoC Creator 4.1 - including a .c file for unit testing

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Anonymous
Not applicable

I'm trying to create a unit test .c file for each of my production code .c files, since I'd like the unit test functions to be in a separate .c file than my production code.  In order to do "white box" unit testing of my functions, I include the .c file I'm testing in the unit test file. 

For example, in my fileToTest_unitTest.c file, I'd have

#include "../src/fileToTest.c"

The code compiles correctly, but I get a lot of linker errors saying that functions and variables have already been defined.  For example:

.\CortexM0\ARM_GCC_541\Debug\fileToTest_unitTest.o: In function `GetCellSerialNum':

fileToTest_unitTest.c:(.text.GetCellSerialNum+0x0): multiple definition of `GetCellSerialNum'

.\CortexM0\ARM_GCC_541\Debug\fileToTest.o:fileToTest.c:(.text.GetCellSerialNum+0x0): first defined here

How do I keep the linker happy in this case?

0 Likes
1 Solution
Anonymous
Not applicable

Just closing the loop on this one.  I did find a workaround the linker problem when I included the .c file under test in my unit test file.  I needed to remove the .c file under test from the project (right-clicking on the c file in Workspace Explorer, then "Remove from <Project>").  In this case, the compiler does not generate two copies of the functions under test. 

Not an elegant workaround, since I will need to add the files under test back into my project later.  And I know that including .c files in other .c files is typically a big no-no.  But this would be the way to do it with PSoC Creator.

View solution in original post

0 Likes
18 Replies
DaKo_463136
Level 5
Level 5
10 likes received 10 likes given 5 likes given

Hello,

Can you post samples of one source file and one test file?

This error just says you can't have the same functions (same name and parameters) in source files, because it is ambiguous, which of the functions should be called.

David

0 Likes
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

Here is a watered-down example of what I'm trying to do.  In my unit tests, I should be able to manipulate variables that are defined in the .c file under test, and then call the functions that operate on them.  Please let me know if I'm overlooking something obvious.

0 Likes
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

Did you look at using one of the existing C unit testing / mocking frameworks (such as CUnit, CMoc, Ceedling)? Usually you separate the test from the regular code, and compile it in a separate step. That way they do no conflict with each other.

Maybe look at this tutorial: https://dmitryfrank.com/articles/unit_testing_embedded_c_applications , I have used it already and it was quite easy. (Although not with PSoC, this was a different MCU)

I think this is the case.

When the test files are removed from the project (and UNIT_TEST_ENABLED directive), the code compiles without any errors or warnings.

Anyway, it's a pity that there is not direct support for unit testing in PSoC Creator.

0 Likes

I'm not aware of any IDE that directly support unit (and mock) testing frameworks. Makefile-based IDEs have a slight advantage since you can influence the list of files that are compiled, so you can exclude your test code there while still having it in the IDE (and be able to edit it there).

I don't know about any for embedded development either. On the other hand, things are moving slowly, but steadily. Top silicon manufacturers do not want to develop their own IDEs from scratch anymore, so we might get to a point, where a single instance of Eclipse or whatever will be used for many MCU families from different manufacturers. This would be a solid ground for one such integrated testing framework. All manufacturers and especially their customers would benefit from that.

I haven't personally started doing unit testing, but I am looking around. However, I tested some important parts of code on PC (I mean x86 compiler), because it is a thousand times faster than on the target HW.

For PCs, InteliJ IDEA and VIsual Studio, for example, support unit tests.

0 Likes
Anonymous
Not applicable

That is a really good tutorial on unit testing.  It looked like he was running his unit test from a host PC and not directly on the target though.  I'm trying to conditionally compile, to either have the target run its unit test or the application code. 

The tutorial seems to demonstrate "black box" unit testing, where you only include header files, then pass parameters into the global functions, and check for the expected return values.  I was able to use this approach on some of my functions, and PSoC Creator didn't have any problems with those.

The problem seems to occur when I want to #include the .c file under test, and set up some of its variables before calling my unit test function.  This is useful when unit testing a static function for example, and I don't want to compile the function as a global function.  This is more like "white box" unit testing, which allows me to unit test static functions without making them global solely for the purpose of unit testing.  I've researched online, and it seems like I *should* be able to get away with including a .c file at the top of my unit test file.  But including the .c file is the part that isn't working with the linker.    

0 Likes

First of all, unit testing, by definition, does not run on the real system. It does not matter whether we are talking about PC software or MCU code, when you do unit testing you test _one_ method in isolation. And isolation means not only other functions but also hardware. This is where the mocking part comes into play - it allows you to define mock version of the stuff that your function-under-test calls so you can control their return values (when needed).

Second: from my understanding _all_ functions in C are global. You expose them through their header files, but thats actually just convenience. So I don't understand your distinction between a 'static' and a 'global' function.

In your test, you include the header files for the functions you want to test. Then you setup the mocks (when you need some), then call your FUT.

Unit tests are normally not used to test functions that are not exposed as API in any form (in C, not being part of any header files). Why should you - the cannot be called by any other module anyway, so they cannot change observable behavior. Just test your API whether it does what it should.

Anonymous
Not applicable

I'm not looking for feedback on my test approach, I'm looking for feedback on the linker error.  I should be able to run a unit test framework on my target platform if I choose to.  This test approach works successfully for the "black box" setup that I described.

In c, I declare functions as static to ensure they can only be called from within the same c file, and they have local scope.  I'm fairly certain this is a common practice by developers.  I don't want to break that guideline just so that my local functions are unit-testable.  I've seen some people work around this be defining a macro for unit testing, but I was trying to avoid that.

I welcome any other feedback or tips on how to include a .c file in another .c file for unit test purposes.

#if UNIT_TEST
#define unit_static
#else
#define unit_static static
#endif

0 Likes

As I said: normally you don't unit-test private (hidden) methods. You unit-test your module API (meaning the function exposed in the header files). These are the methods you need to care about, and make sure they behave correctly. Private methods will be tested by calling them from the public ones.

So you don't need to include the source code of your module into the test code.

Anonymous
Not applicable

As I said: I'm not looking for feedback on my test approach, I'm looking for feedback on the linker error.  I can imagine cases where unit testing static functions directly would be useful.  For example, a thread with an incoming/outgoing message queue, which is it's only link to the rest of the world.  Within that thread, static functions 30 calls deep.  I'd have more confidence in my code if I could test each static function individually.

I welcome any other feedback or tips on how to include a .c file in another .c file, and avoid linker problems when parent .c file calls functions from child .c file...

0 Likes

#include-ing a source file into another essentially duplicates the code, so the linker is correct to complain about duplicated functions. Your only solution is to do the same as done with header files (which have the same problem): use #ifdef's.

Since the #include is done by the precompiler, the compiler and the linker just see two functions with the same name, and thats not allowed.

Either that or you re-structure your code to make it better testable (which, in my experience make the code better designed in most cases [but not all, I admit that]).

lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

I appreciate your insight on this problem.  I agree that this should work, and as part of my debugging before posting, I did try to add header guards to my .c file and it still didn't link properly.  I attached an updated version of my watered down example code with a header guard in the included .c file.  Still not sure why this simple example isn't linking properly. 

Using header guards will not solve the problem. They are designed for preventing _the same_ header file to be included _twice_ in the same source file (because then the compiler complains that function are redefined).

But in your case, you need to make sure that the same function does not appear twice _in the whole source tree_. So you need to distinguish between regular compilation and compiling tests (assuming that each source file has exactly one test file).

I think you can, somewhere in the build properties, configure that intermediate files are not deleted. Maybe its a GCC config. When you do so, you can inspect what the preprocessor is doing. Then look at the preprocessed files, and then think about what the compiler and the linker will do with the result.

Anonymous
Not applicable

I thought you were suggesting to use header guards in the .c file from your previous post:

#include-ing a source file into another essentially duplicates the code, so the linker is correct to complain about duplicated functions. Your only solution is to do the same as done with header files (which have the same problem): use #ifdef's.

Since the #include is done by the precompiler, the compiler and the linker just see two functions with the same name, and thats not allowed.

But thanks for the insight on controlling GCC to not delete intermediate files.  I'll dig through the options that are presented in PSoC Creator and post what I find.

0 Likes
Anonymous
Not applicable

Just an update - I didn't find any user-accessible options on PSoC Creator, but added "-save-temps" to the compiler command line.  This generated the same results as before, still not able to get my simple example to link properly.

0 Likes
Anonymous
Not applicable

Just closing the loop on this one.  I did find a workaround the linker problem when I included the .c file under test in my unit test file.  I needed to remove the .c file under test from the project (right-clicking on the c file in Workspace Explorer, then "Remove from <Project>").  In this case, the compiler does not generate two copies of the functions under test. 

Not an elegant workaround, since I will need to add the files under test back into my project later.  And I know that including .c files in other .c files is typically a big no-no.  But this would be the way to do it with PSoC Creator.

0 Likes

When you use one of the already mentioned tools for unit-testing, they will do all the work on the command line anyway. Including compiling your code, so you don't really need it in the IDE (apart from getting the nice editor).

0 Likes