[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-devel
Subject:    Re: RFC: Library unit testing and symbol visibility
From:       Lukas Sommer <sommerluk () gmail ! com>
Date:       2023-07-24 9:22:27
Message-ID: CAFTrL-0=5yVWmYiSQaf4QKhh_zirRyg=qFuePoU0=ubfpHgfXA () mail ! gmail ! com
[Download RAW message or body]

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/e82c2a62ccd93d9f0f3=
1153e05d80ef746d37806/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=C3=BCns <
stefan.bruens@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 i=
s
> 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 LT=
O.
>
> 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 testin=
g
> 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
>
>

[Attachment #3 (text/html)]

<div dir="ltr"><div dir="ltr">Another approach involves building the library twice: \
once as a &quot;normal&quot; build and once as an &quot;internal&quot; build. To \
implement this, you can create two CMake targets, &quot;mylibrary&quot; and \
&quot;mylibrary_internal,&quot; 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.<br><br>Instead of \
using:<div><br></div><div><pre lang="cmake"><span \
id="m_-3601454869079256072m_5609525288502381631m_-1763255687690334495gmail-LC217" \
lang="cmake"><span>    CXX_VISIBILITY_PRESET \
</span><span>&quot;hidden&quot;</span><span></span></span> <span \
id="m_-3601454869079256072m_5609525288502381631m_-1763255687690334495gmail-LC218" \
lang="cmake"><span>    VISIBILITY_INLINES_HIDDEN </span><span>TRUE</span></span> \
</pre></div><div><br></div><div>Opt for:</div><div><br></div><div><pre \
lang="cmake"><span id="m_-3601454869079256072m_5609525288502381631m_-1763255687690334495gmail-LC321" \
lang="cmake"><span>    CXX_VISIBILITY_PRESET \
</span><span>&quot;default&quot;</span><span></span></span> <span \
id="m_-3601454869079256072m_5609525288502381631m_-1763255687690334495gmail-LC322" \
lang="cmake"><span>    VISIBILITY_INLINES_HIDDEN \
</span><span>FALSE</span></span></pre></div><div><br></div><div>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).</div><br><div>Advantages of this approach:</div><div>+  No need to add \
extra export macros for the internal API; you can keep your source code as it \
is.</div><div>+ All symbols are readily available out-of-the-box.</div><div>+ 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.</div><div>+ 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.</div><div><br></div><div>Drawbacks:<div></div><div>- The library \
will be built twice, once for the internal build and once for the normal \
build.</div><div>- The approach is not \
elegant.</div></div><div><br></div><div>Currently, I am using this approach, which \
can be found in the CMake code here:</div><div><br></div><div><a \
href="https://invent.kde.org/libraries/perceptualcolor/-/blob/e82c2a62ccd93d9f0f31153e05d80ef746d37806/src/CMakeLists.txt#L317" \
target="_blank">https://invent.kde.org/libraries/perceptualcolor/-/blob/e82c2a62ccd93d \
9f0f31153e05d80ef746d37806/src/CMakeLists.txt#L317</a></div><div><br></div><div>While \
it works well for me, I&#39;m not entirely certain if this is the &quot;preferred \
option&quot; in general. Nevertheless, it has proven to be effective for my use \
case.</div><div><br></div><div>(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 &quot;normal&quot; build. \
However, it is possible that simply I made errors in the CMake \
code.)</div><div><br></div><div>Best regards,<br></div><div><br></div><div><div><div \
dir="ltr" class="gmail_signature">Lukas Sommer</div></div><br></div></div><br><div \
class="gmail_quote"><div dir="ltr" class="gmail_attr">Am So., 23. Juli 2023 um 14:16  \
Uhr schrieb Stefan BrĂ¼ns &lt;<a href="mailto:stefan.bruens@rwth-aachen.de" \
target="_blank">stefan.bruens@rwth-aachen.de</a>&gt;:<br></div><blockquote \
class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid \
rgb(204,204,204);padding-left:1ex">Hi,<br> <br>
I am looking for some feedback regarding unit testing libraries (e.g. <br>
Frameworks libraries, but not limited to). First, some background:<br>
<br>
As long as on limits oneself to testing the public API of a library all is <br>
easy, linking and calling the exposed API just works. Some say that&#39;s all that \
<br> should be tested, case closed.<br>
<br>
Unfortunately, in my experience that does not work out all the time.<br>
<br>
Unit testing requires some input data, and it is not always possible to <br>
provide the required data to the exposed API. One example would be files, <br>
files can be large, especially if there are many variations which need to be <br>
covered. Another one is extended attributes, e.g. KFileMetaData testing <br>
requires a writable filesystem with XAttr support (tmpfs only has limited <br>
XAttr support). The same probably applies to DBs and external (Web) servers, <br>
or anything that involves system calls.<br>
<br>
Public API may also limit the possibility to inject errors, or good coverage <br>
may be limited by combinatorial explosion.<br>
<br>
So there a definitely good reasons to not only test public API, but also <br>
internal functions.<br>
<br>
Hiding just the API is fairly simple, just make the relevant header private.<br>
<br>
But preferably, also the corresponding symbols in the created shared libraries <br>
are hidden. Hiding unexported/private symbols reduces runtime link time, and <br>
allows the compiler to remove unreachable code, due to inlining and/or LTO.<br>
<br>
On the other hand, these hidden functions can also no longer be linked to and <br>
called by any unit tests.<br>
<br>
<br>
So, now to my actual question - what are the preferred options for testing <br>
internal API?<br>
<br>
1. Just link the test binary to the relevant source files<br>
   + Trivial<br>
   - May require long lists of sources, extra `target_include_directories` etc.<br>
<br>
2. Create an intermediate CMake OBJECT library, which is linked to by the test <br>
binaries, and used for the exported shared library. The object library is not <br>
affected by symbol visibility<br>
   + CMake properties like include directories still work<br>
   - adds an extra intermediate target, less idiomatic<br>
<br>
3. ?<br>
<br>
Kind regards,<br>
<br>
Stefan<br>
<br>
</blockquote></div>
</div>



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic