Skip to content

Commit e9c6577

Browse files
Feature/unit tests (#44)
* Add basic test framework to run unit tests via googletest * Update README.md to show how to disable building the unit tests * Add instruction how to run the unit tests * Add GitHub action to trigger unit tests * Trigger GitHub action * Trigger GitHub action for unit tests for each pull request against the development branch * Add some auto generated unit tests for the Transmitter * Update README.md --------- Co-authored-by: Jordi J. Gimenez <[email protected]>
1 parent dcff4a5 commit e9c6577

File tree

5 files changed

+228
-14
lines changed

5 files changed

+228
-14
lines changed

.github/workflows/unit-tests.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Unit Tests
2+
3+
on:
4+
pull_request:
5+
branches: [development]
6+
7+
concurrency:
8+
group: unit-tests-${{ github.ref }}
9+
cancel-in-progress: true
10+
11+
jobs:
12+
build-and-test:
13+
name: Build & Test (Ubuntu)
14+
runs-on: ubuntu-latest
15+
env:
16+
CTEST_OUTPUT_ON_FAILURE: 1
17+
CCACHE_DIR: ${{ github.workspace }}/.ccache
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Install dependencies
23+
run: |
24+
sudo apt-get update
25+
sudo apt-get install -y --no-install-recommends \
26+
ninja-build build-essential cmake \
27+
libboost-all-dev libspdlog-dev libtinyxml2-dev libconfig++-dev \
28+
libssl-dev libnl-3-dev zlib1g-dev ccache clang-tidy clang g++-12
29+
30+
- name: Configure (CMake)
31+
run: |
32+
cmake -S . -B build -GNinja -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release
33+
34+
- name: Build
35+
run: cmake --build build --parallel
36+
37+
- name: List tests (debug)
38+
run: ctest --test-dir build/tests -N
39+
40+
- name: Run unit tests
41+
run: |
42+
set -o pipefail
43+
ctest --test-dir build/tests --output-on-failure -j 2 | tee build/ctest-log.txt
44+
45+
- name: Upload test log
46+
if: always()
47+
uses: actions/upload-artifact@v4
48+
with:
49+
name: ctest-log
50+
path: build/ctest-log.txt
51+
if-no-files-found: warn

CMakeLists.txt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ pkg_check_modules(LIBCONFIG REQUIRED IMPORTED_TARGET libconfig++)
2020
pkg_check_modules(ZLIB REQUIRED IMPORTED_TARGET zlib)
2121
check_cxx_symbol_exists(mmap "sys/mman.h" HAVE_MMAP)
2222

23-
add_subdirectory(examples)
23+
# Option to build example programs
24+
option(BUILD_EXAMPLES "Build example transmitter/receiver programs" ON)
25+
if (BUILD_EXAMPLES)
26+
add_subdirectory(examples)
27+
endif()
2428

2529
include_directories(
2630
"${PROJECT_BINARY_DIR}"
@@ -56,8 +60,6 @@ target_include_directories(flute
5660
${CMAKE_CURRENT_LIST_DIR}/include/
5761
)
5862

59-
#add_library(flute src/Receiver.cpp src/Receiver.h src/AlcPacket.cpp src/File.cpp src/EncodingSymbol.cpp src/FileDeliveryTable.cpp)
60-
6163
target_link_libraries( flute
6264
LINK_PUBLIC
6365
spdlog::spdlog
@@ -69,3 +71,8 @@ target_link_libraries( flute
6971
PkgConfig::ZLIB
7072
)
7173

74+
# ---- Tests Subdirectory (optional) ----
75+
option(BUILD_TESTING "Build unit tests" ON)
76+
if (BUILD_TESTING AND NOT DEFINED GTEST_DISABLE)
77+
add_subdirectory(tests)
78+
endif()

README.md

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,98 +6,139 @@
66
</p>
77

88
## Introduction
9+
910
Additional information can be found at: https://5g-mag.github.io/Getting-Started/pages/multimedia-content-delivery/
1011

1112
## Installation guide
1213

1314
Installation of libflute consists of 4 simple steps:
15+
1416
1. Getting the source code
1517
2. Installing the dependencies
1618
3. Build setup
1719
4. Building
1820

1921
### Step 1: Getting the source code
22+
2023
````
2124
cd ~
2225
git clone https://github.com/5G-MAG/rt-libflute.git
2326
````
2427

2528
### Step 2: Installing the dependencies
29+
2630
````
2731
sudo apt install ninja-build libboost-all-dev libspdlog-dev libtinyxml2-dev libconfig++-dev clang-tidy clang g++-12
2832
````
2933

3034
### Step 3: Build setup
35+
3136
````
3237
cd rt-libflute/
3338
mkdir build && cd build
3439
cmake -GNinja ..
3540
````
3641

42+
If you want to build the project without the unit tests run the following commands instead:
43+
44+
````
45+
cd rt-libflute/
46+
mkdir build && cd build
47+
cmake -GNinja -DBUILD_TESTING=OFF ..
48+
````
49+
3750
### Step 3: Building
51+
3852
````
3953
ninja
4054
````
4155

4256
## Usage
43-
44-
When installing libflute, it comes with two demo applications, a receiver and a transmitter. Both applications can be found under ``libflute/build/examples``.
57+
58+
When installing libflute, it comes with two demo applications, a receiver and a transmitter. Both applications can be
59+
found under ``rt-libflute/build/examples``.
4560

4661
### Step 1: Setting up a Flute receiver
62+
4763
To start the Flute receiver type in
4864

4965
````
66+
cd rt-libflute/build/examples
5067
./flute-receiver
5168
````
5269

53-
The application will listen at the multicast address 238.1.1.95 by default. Check the help page for additional options (``./flute-receiver --help``).
70+
The application will listen at the multicast address 238.1.1.95 by default. Check the help page for additional options (
71+
``./flute-receiver --help``).
5472

5573
### Step 2: Setting up a Flute transmitter
5674

5775
To start the Flute transmitter type in
5876

5977
````
78+
cd rt-libflute/build/examples
6079
./flute-transmitter -r 100000 file
6180
````
6281

6382
For file enter a file that shall be transmitted.
64-
83+
6584
The parameter -r provides a data rate limit in kbit/s.
6685

67-
> **Note:** Keep in mind, the rate limit should not be set higher than the network allows, otherwise packet loss can occur (UDP transmission).
86+
> **Note:** Keep in mind, the rate limit should not be set higher than the network allows, otherwise packet loss can
87+
> occur (UDP transmission).
6888
6989
### Optional: Using IPSec for secure transmission
90+
7091
If you want to ensure, that transmission between to parties shall be encrypted, you can activate IPSec.
71-
72-
Simply use the -k parameter on transmitter and receiver side with a. As IPSec key a AES 256-bit key (so 64 character long) is expected.
7392

74-
* Starting the receiver with IPSec key:
93+
Simply use the -k parameter on transmitter and receiver side with a. As IPSec key a AES 256-bit key (so 64 character
94+
long) is expected.
95+
96+
* Starting the receiver with IPSec key:
97+
7598
````
7699
sudo ./flute-receiver -k fdce8eaf81e3da02fa67e07df975c0111ecfa906561e762e5f3e78dfe106498e
77100
````
78-
As soon as the receiver is starting with -k option, a policy is beeing created that ensures that incoming packets with a specific destination address (can be set with -m) are decrypted with the specified IPSec key.
101+
102+
As soon as the receiver is starting with -k option, a policy is beeing created that ensures that incoming packets with a
103+
specific destination address (can be set with -m) are decrypted with the specified IPSec key.
79104

80105
You can check the policies with
106+
81107
````
82108
sudo ip xfrm state list
83109
sudo ip xfrm policy list
84110
````
85111

86112
* Starting the transmitter with IPSec key:
113+
87114
````
88115
sudo ./flute-transmitter -r 100000 -k fdce8eaf81e3da02fa67e07df975c0111ecfa906561e762e5f3e78dfe106498e file
89116
````
90-
Outgoing packages with a specific destination address (can be set with -m) will be encrypted with the specified IPSec key.
117+
118+
Outgoing packages with a specific destination address (can be set with -m) will be encrypted with the specified IPSec
119+
key.
91120

92121
* Optional: Setting superuser rights
93122

94-
To allow the application to set policy entries without superuser privileges for IPSec, set its capabilities
123+
To allow the application to set policy entries without superuser privileges for IPSec, set its capabilities
95124
accordingly. Alternatively, you can run it with superuser rights (``sudo ...``).
125+
96126
````
97127
sudo setcap 'cap_net_admin=eip' ./flute-transmitter
98128
sudo setcap 'cap_net_admin=eip' ./flute-receiver
99129
````
100130

131+
## Testing
132+
133+
To execute the unit tests make sure to have built the project with the unit tests enabled (see Step 3: Build setup).
134+
135+
Then run
136+
137+
````
138+
cd build/tests
139+
ctest
140+
````
141+
101142
## Documentation
102143

103144
Documentation of the source code can be found at: https://5g-mag.github.io/rt-libflute/

tests/CMakeLists.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# CMake configuration for unit tests
2+
# Assumes parent CMakeLists.txt set option(BUILD_TESTING ...)
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
# GoogleTest requires at least C++17
6+
set(CMAKE_CXX_STANDARD 17)
7+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
8+
9+
include(FetchContent)
10+
11+
# Fetch GoogleTest only if not already provided and not explicitly disabled
12+
13+
FetchContent_Declare(
14+
googletest
15+
GIT_REPOSITORY https://github.com/google/googletest.git
16+
GIT_TAG v1.17.0
17+
GIT_SHALLOW TRUE
18+
)
19+
20+
# For Windows: Prevent overriding the parent project's compiler/linker settings
21+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
22+
FetchContent_MakeAvailable(googletest)
23+
24+
enable_testing()
25+
26+
add_executable(flute_tests
27+
test_transmitter.cpp
28+
)
29+
30+
# Link against library under test
31+
target_link_libraries(
32+
flute_tests
33+
PRIVATE
34+
flute
35+
GTest::gtest_main
36+
)
37+
38+
target_include_directories(flute_tests PRIVATE ${PROJECT_SOURCE_DIR}/include)
39+
include(GoogleTest)
40+
gtest_discover_tests(flute_tests)

tests/test_transmitter.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include <gtest/gtest.h>
2+
#include <boost/asio.hpp>
3+
#include "Transmitter.h"
4+
5+
using namespace LibFlute;
6+
7+
// Helper to construct a Transmitter for tests
8+
static std::unique_ptr<Transmitter> make_tx(boost::asio::io_context &io, uint32_t rate_limit = 0) {
9+
// Use a multicast address and reasonable MTU
10+
return std::make_unique<Transmitter>("239.1.1.1", 5000, /*tsi*/1234, /*mtu*/1400, rate_limit, io);
11+
}
12+
13+
TEST(TransmitterGetterSetterTest, RateLimitGetterSetter) {
14+
boost::asio::io_context io;
15+
auto tx = make_tx(io, 0);
16+
// Initial value should match constructor
17+
EXPECT_EQ(tx->rate_limit(), 0u);
18+
// Set new value
19+
tx->rate_limit(1500);
20+
EXPECT_EQ(tx->rate_limit(), 1500u);
21+
// Chaining
22+
tx->rate_limit(2000).rate_limit(3000);
23+
EXPECT_EQ(tx->rate_limit(), 3000u);
24+
}
25+
26+
TEST(TransmitterGetterSetterTest, EndpointSetterString) {
27+
boost::asio::io_context io;
28+
auto tx = make_tx(io);
29+
// Initial endpoint
30+
auto ep_initial = tx->endpoint();
31+
EXPECT_EQ(ep_initial.address().to_string(), std::string("239.1.1.1"));
32+
EXPECT_EQ(ep_initial.port(), 5000);
33+
// Change via string overload
34+
tx->endpoint("239.1.1.2", 6000);
35+
auto ep_new = tx->endpoint();
36+
EXPECT_EQ(ep_new.address().to_string(), std::string("239.1.1.2"));
37+
EXPECT_EQ(ep_new.port(), 6000);
38+
}
39+
40+
TEST(TransmitterGetterSetterTest, EndpointSetterEndpointObject) {
41+
boost::asio::io_context io;
42+
auto tx = make_tx(io);
43+
boost::asio::ip::udp::endpoint new_ep(boost::asio::ip::make_address("239.1.1.3"), 7000);
44+
tx->endpoint(new_ep);
45+
auto ep = tx->endpoint();
46+
EXPECT_EQ(ep.address().to_string(), std::string("239.1.1.3"));
47+
EXPECT_EQ(ep.port(), 7000);
48+
// Move overload
49+
boost::asio::ip::udp::endpoint moved_ep(boost::asio::ip::make_address("239.1.1.4"), 8000);
50+
tx->endpoint(std::move(moved_ep));
51+
auto ep2 = tx->endpoint();
52+
EXPECT_EQ(ep2.address().to_string(), std::string("239.1.1.4"));
53+
EXPECT_EQ(ep2.port(), 8000);
54+
}
55+
56+
TEST(TransmitterGetterSetterTest, UdpTunnelAddressSetAndUnset) {
57+
boost::asio::io_context io;
58+
auto tx = make_tx(io);
59+
// Initially no tunnel endpoint
60+
EXPECT_FALSE(tx->udp_tunnel_address().has_value());
61+
// Set tunnel endpoint (copy overload)
62+
boost::asio::ip::udp::endpoint tunnel_ep(boost::asio::ip::make_address("127.0.0.1"), 9000);
63+
tx->udp_tunnel_address(tunnel_ep);
64+
ASSERT_TRUE(tx->udp_tunnel_address().has_value());
65+
EXPECT_EQ(tx->udp_tunnel_address()->address().to_string(), std::string("127.0.0.1"));
66+
EXPECT_EQ(tx->udp_tunnel_address()->port(), 9000);
67+
// Set tunnel endpoint (move overload) to a new value
68+
boost::asio::ip::udp::endpoint tunnel_ep2(boost::asio::ip::make_address("127.0.0.1"), 9100);
69+
tx->udp_tunnel_address(std::move(tunnel_ep2));
70+
ASSERT_TRUE(tx->udp_tunnel_address().has_value());
71+
EXPECT_EQ(tx->udp_tunnel_address()->port(), 9100);
72+
// Unset
73+
tx->udp_tunnel_address(std::nullopt);
74+
EXPECT_FALSE(tx->udp_tunnel_address().has_value());
75+
}

0 commit comments

Comments
 (0)