sheredom / utest.h

🧪 single header unit testing framework for C and C++
The Unlicense
834 stars 57 forks source link

Test does not run from inside statically linked library (gcc) #141

Open aganm opened 10 months ago

aganm commented 10 months ago

I started organizing groups of related test files into their own static libraries such that all the static libraries will get linked together into a single test executable. The reason for this is because I need to compile multiple test executables which have different characteristics, which does not affect test code, but can affect the code under test. So instead of recompiling the tests everytime, I want to put the tests in a static library which will be reused for all of my test executables, cutting down my compile times by a significant amount.

The issue is that test cases that are inside a static library do not get run from the executable's UTEST_MAIN.

I tested __attribute__((constructor)) from inside a static library and the attribute works as expected. Could it be something with UTEST global arrays not being shared across the static library and the executable?

Edit: I think I tested the __attribute__((constructor)) wrong, I'm trying it again and it won't run anymore. I'm getting hella confused.

Edit 2: Okay.. I think I know what is going on here.

  1. main executable + static library __attribute__((constructor)) function, the constructor does not run.

  2. main executable + static library __attribute__((constructor)) + regular function, when the main executable calls the regular function from the same file as the constructor in the static library, the constructors inside the static library get called!

So the solution would seem to be: put a bootstrap function inside each test file in the static library, and call each bootstrap function from the executable to trigger the constructors in the static library.

Ooooff.

sheredom commented 10 months ago

I'll guess it is something with the constructors yeah. We'll need a test case for this probably, so I'll keep the issue around for when I have the energy.

aganm commented 7 months ago

I found the solution to my problem on this stack overflow answer .

unreferenced symbols from archive does not make it to the output binary, because linker discards them by default.

So it's not a constructor attribute issue, any translation unit that does not get explicitly called to will get discarded by the linker. But there is an easier solution than what I did initially:

To override this behaviour when linking with static library, --whole-archive/--no-whole-archive options for the linker may be used [...] This may lead to bloated binary, because all symbols from foo.a will be included by the linker to the output, but sometimes it is justified.

Not sure what I'll do about this because I've already spent the time updating my tests with bootstrap functions as explained in my original message. But at the same time I would gladly delete all this bootstrap boilerplate.

I should write a note about this in the readme.

sheredom commented 7 months ago

I wonder if we can mark the symbol with attribute((used)) or something to stop it ripping them out?

aganm commented 7 months ago

I tried attribute((used)) to no avail. Thank you for your suggestion nonetheless!

Also I might have to backtrack on the stack overflow solution --whole-archive, I can't get it to work in my larger project. 😫

Bootstraps will remain for now. Until a better solution comes up.