The purpose of our unit tests is to verify that the various
features work across a number of different platforms and
compilers. Having an extensive unit test suite makes it
easier for developers to ensure that their changes work as expected
and do not cause regressions in unexpected areas of the library.

The goal is that all features in a library should have a corresponding test
case. Often a test case is added while implementing a new
feature. This makes it possible to check that the new functionality
works as expected as early as possible.

The unit tests are implemented using the Google C++ Testing Framework
(gtest) which defines a bunch of helpers that are useful when writing
tests. You can find more information on gtest on their homepage.

Running the unit tests may take a long time on mobile and embedded
platforms, since we test with an extensive set of parameters.
For some projects, we defined the embedded profile to lower
the complexity and speed up the tests:

The purpose of this is to make it easy to find the unit test for a
specific class. In some cases it makes sense to have multiple classes
tested in the same file. In those cases we still make a placeholder
.cpp file referring to the actual .cpp file where the test can be
found. An example of this can be seen for some of the codecs, e.g. the
class encoder located in src/kodo/rlnc/encoder.hpp is tested in
test_coders.cpp but the place holder file still exists.

The placeholder file in this case
(test/src/test_encoder.cpp) looks like the following:

The Kodo library is build using the
parameterized-inheritance/ mixin-layers C++ design
technique. When unit testing a layer we try to isolate it as much as
possible. To do this we typically introduce dummy layers with the sole
purpose of satisfying the layer’s dependencies. To see this in action
let’s look at one of the existing unit tests.

The storage_bytes_used layer is used when we want add
functionality allowing us to keep track of how many useful bytes an
encoder or decoder contains.

Note

In general the amount of data which can be encoded or
decoded will be determined by the number of symbols we are
coding and the size of every symbol in bytes (we call this
the block size). However, in practical applications we
sometimes do not have enough data to fill an entire
block. In those cases we can add the storage_bytes_used
layer to embed in every encoder and decoder the ability to
store the number of actual data bytes.

// Copyright Steinwurf ApS 2011.// Distributed under the "STEINWURF EVALUATION LICENSE 1.0".// See accompanying file LICENSE.rst or// http://www.steinwurf.com/licensing#pragma once#include<cassert>#include<cstdint>namespacekodo_core{/// @ingroup storage_info_layers////// @brief Provides access to the number of useful bytes used out/// of the total size of the encoders or decoders storage.template<classSuperCoder>classstorage_bytes_used:publicSuperCoder{public:/// Constructorstorage_bytes_used():m_bytes_used(0){}/// @copydoc layer::initialize()template<classFactory>voidinitialize(Factory&the_factory){SuperCoder::initialize(the_factory);m_bytes_used=0;}/// @copydoc layer::set_bytes_used()voidset_bytes_used(uint32_tbytes_used){assert(bytes_used>0);assert(bytes_used<=SuperCoder::block_size());m_bytes_used=bytes_used;}/// @copydoc layer::bytes_used()uint32_tbytes_used()const{returnm_bytes_used;}protected:/// The number of bytes useduint32_tm_bytes_used;};}

As seen, the layer depends on two functions being provided by the
SuperCoder:

SuperCoder::initialize(the_factory)

SuperCoder::block_size()

Using our project documentation, it is possible to look up the
purpose of the two undefined functions.

In this case we want to check that the state is correctly updated when
calling set_bytes_used and that the state is correctly reset when
calling initialize. The following unit test code was implemented
in test/src/test_storage_bytes_used.cpp to test this:

When we define a test using gtest we use the TEST(test_case_name,test_name)
macro to define and name a test function. We follow this naming guideline:

The test_case_name should match the name of the .cpp file. For example,
test_my_fancy_code.cpp contains test_my_fancy_code test cases.
This should make it trivial to find the .cpp if a unit test fails.
If a project contains multiple classes with the same name (in different
subfolders), then it is recommended to modify those class names.
If that is not possible, then we can add the subfolder as a prefix to the
test_case_name, e.g. subfolder_test_my_fancy_code.
Remember to place the test files as described in
Namespaces and directories

The test_name is up to the developer, but should be as
descriptive of the purpose of the unit test as possible.