RFC: Library unit testing and symbol visibility

Lukas Sommer sommerluk at gmail.com
Mon Jul 24 10:22:27 BST 2023


Another approach involves building the library twice: once as a "normal"
build and once as an "internal" build. To implement this, you can create
two CMake targets, "mylibrary" and "mylibrary_internal," while utilizing
the same headers and source files for both targets. However, for the
internal target, certain CMake target properties need to be set differently
from the normal target.

Instead of using:

    CXX_VISIBILITY_PRESET "hidden"    VISIBILITY_INLINES_HIDDEN TRUE


Opt for:

    CXX_VISIBILITY_PRESET "default"    VISIBILITY_INLINES_HIDDEN FALSE


By doing so, all symbols should be exported on Linux without the need to
add extra export macros in your source code (although on Windows,
additional CMake settings are necessary).

Advantages of this approach:
+ No need to add extra export macros for the internal API; you can keep
your source code as it is.
+ All symbols are readily available out-of-the-box.
+ Faster build times compared to proposal number 1: All unit tests build
against the same internal library, eliminating the need to rebuild
individual sources repeatedly.
+ Easier compared to proposal number 1: No need to worry about source file
dependencies since the entire library is built as a whole, making every
symbol available.

Drawbacks:
- The library will be built twice, once for the internal build and once for
the normal build.
- The approach is not elegant.

Currently, I am using this approach, which can be found in the CMake code
here:

https://invent.kde.org/libraries/perceptualcolor/-/blob/e82c2a62ccd93d9f0f31153e05d80ef746d37806/src/CMakeLists.txt#L317

While it works well for me, I'm not entirely certain if this is the
"preferred option" in general. Nevertheless, it has proven to be effective
for my use case.

(Previously, I attempted something similar to your proposal number 2 to
avoid building twice. Unfortunately, I encountered issues, such as a
significantly larger binary size in the resulting "normal" build. However,
it is possible that simply I made errors in the CMake code.)

Best regards,

Lukas Sommer


Am So., 23. Juli 2023 um 14:16 Uhr schrieb Stefan BrĂ¼ns <
stefan.bruens at rwth-aachen.de>:

> Hi,
>
> I am looking for some feedback regarding unit testing libraries (e.g.
> Frameworks libraries, but not limited to). First, some background:
>
> As long as on limits oneself to testing the public API of a library all is
> easy, linking and calling the exposed API just works. Some say that's all
> that
> should be tested, case closed.
>
> Unfortunately, in my experience that does not work out all the time.
>
> Unit testing requires some input data, and it is not always possible to
> provide the required data to the exposed API. One example would be files,
> files can be large, especially if there are many variations which need to
> be
> covered. Another one is extended attributes, e.g. KFileMetaData testing
> requires a writable filesystem with XAttr support (tmpfs only has limited
> XAttr support). The same probably applies to DBs and external (Web)
> servers,
> or anything that involves system calls.
>
> Public API may also limit the possibility to inject errors, or good
> coverage
> may be limited by combinatorial explosion.
>
> So there a definitely good reasons to not only test public API, but also
> internal functions.
>
> Hiding just the API is fairly simple, just make the relevant header
> private.
>
> But preferably, also the corresponding symbols in the created shared
> libraries
> are hidden. Hiding unexported/private symbols reduces runtime link time,
> and
> allows the compiler to remove unreachable code, due to inlining and/or LTO.
>
> On the other hand, these hidden functions can also no longer be linked to
> and
> called by any unit tests.
>
>
> So, now to my actual question - what are the preferred options for testing
> internal API?
>
> 1. Just link the test binary to the relevant source files
>   + Trivial
>   - May require long lists of sources, extra `target_include_directories`
> etc.
>
> 2. Create an intermediate CMake OBJECT library, which is linked to by the
> test
> binaries, and used for the exported shared library. The object library is
> not
> affected by symbol visibility
>   + CMake properties like include directories still work
>   - adds an extra intermediate target, less idiomatic
>
> 3. ?
>
> Kind regards,
>
> Stefan
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.kde.org/pipermail/kde-devel/attachments/20230724/5509b766/attachment-0001.htm>


More information about the kde-devel mailing list