tests: Add initial unit tests

These tests are mostly just placeholders until something more
substantial is added. They are good tests, but it would be better to
also test the functions in different kinds of scenarios.

Note: the Crypto++ is causing problems in tests on Windows, because the
CMake is rerun often and this causes the Crypto++ linking to disappear.
This commit is contained in:
Joni Räsänen 2021-09-07 12:23:40 +03:00
parent ea55f655ea
commit 5fe0116327
8 changed files with 399 additions and 8 deletions

View File

@ -10,3 +10,4 @@ config
doc
build
common
tests

View File

@ -9,7 +9,7 @@
#include "debug.hh" // debug prints
#include "frame.hh" // frame related functions
#include "util.hh" // types
#include "version.hh"
#include "version.hh" // version
#include <map>
#include <string>

View File

@ -7,7 +7,10 @@ add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME}
PRIVATE
main.cpp
TestGTest.cpp
tests_encryption.cpp
tests_rtcp.cpp
tests_rtp.cpp
tests_version.cpp
)
target_link_libraries(${PROJECT_NAME}
PRIVATE

View File

@ -1,6 +0,0 @@
#include "lib.hh"
#include <gtest/gtest.h>
TEST(DefaultTest, ctor) {
EXPECT_STREQ("2.0.1", uvgrtp::get_version().c_str());
}

138
test/tests_encryption.cpp Normal file
View File

@ -0,0 +1,138 @@
#include "lib.hh"
#include <gtest/gtest.h>
// network parameters of example
constexpr char SENDER_ADDRESS[] = "127.0.0.1";
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char RECEIVER_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
constexpr auto EXAMPLE_DURATION = std::chrono::seconds(1);
// encryption parameters of example
enum Key_length { SRTP_128 = 128, SRTP_196 = 196, SRTP_256 = 256 };
constexpr Key_length KEY_SIZE = SRTP_256;
constexpr int KEY_SIZE_BYTES = KEY_SIZE / 8;
constexpr int SALT_SIZE = 112;
constexpr int SALT_SIZE_BYTES = SALT_SIZE / 8;
void process_frame(uvgrtp::frame::rtp_frame* frame);
void receive_func(uint8_t key[KEY_SIZE_BYTES], uint8_t salt[SALT_SIZE_BYTES]);
std::unique_ptr<std::thread> user_initialization(uvgrtp::context& ctx, Key_length sha,
uvgrtp::session* sender_session, uvgrtp::media_stream* send);
TEST(EncryptionTests, no_send_user) {
uvgrtp::context ctx;
uvgrtp::session* sender_session = nullptr;
uvgrtp::media_stream* send = nullptr;
std::unique_ptr<std::thread> receiver;
receiver = user_initialization(ctx, SRTP_256, sender_session, send);
// All media is now encrypted/decrypted automatically
char* message = (char*)"Hello, world!";
size_t msg_len = strlen(message);
if (send)
{
auto start = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < 10; ++i)
{
EXPECT_EQ(RTP_OK, send->push_frame((uint8_t*)message, msg_len, RTP_NO_FLAGS));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
sender_session->destroy_stream(send);
if (receiver && receiver->joinable())
{
receiver->join();
}
if (sender_session)
ctx.destroy_session(sender_session);
}
std::unique_ptr<std::thread> user_initialization(uvgrtp::context& ctx, Key_length sha,
uvgrtp::session* sender_session, uvgrtp::media_stream* send)
{
uint8_t key[KEY_SIZE_BYTES] = { 0 };
uint8_t salt[SALT_SIZE_BYTES] = { 0 };
// initialize SRTP key and salt with dummy values
for (int i = 0; i < KEY_SIZE_BYTES; ++i)
key[i] = i;
for (int i = 0; i < SALT_SIZE_BYTES; ++i)
salt[i] = i * 2;
// Enable SRTP and let user manage the keys
unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER | RCE_SRTP_KEYSIZE_256;
sender_session = ctx.create_session(RECEIVER_ADDRESS);
send = sender_session->create_stream(LOCAL_PORT, REMOTE_PORT, RTP_FORMAT_GENERIC, flags);
EXPECT_NE(nullptr, sender_session);
EXPECT_NE(nullptr, send);
if (send)
send->add_srtp_ctx(key, salt); // add user context
return std::unique_ptr<std::thread>(new std::thread(receive_func, key, salt));
}
void receive_func(uint8_t key[KEY_SIZE_BYTES], uint8_t salt[SALT_SIZE_BYTES])
{
/* See sending.cc for more details */
uvgrtp::context ctx;
uvgrtp::session* receiver_session = ctx.create_session(SENDER_ADDRESS);
EXPECT_NE(nullptr, receiver_session);
/* Enable SRTP and let user manage keys */
unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER;
flags |= RCE_SRTP_KEYSIZE_256;
/* See sending.cc for more details about create_stream() */
uvgrtp::media_stream* recv = receiver_session->create_stream(REMOTE_PORT, LOCAL_PORT,
RTP_FORMAT_GENERIC, flags);
EXPECT_NE(nullptr, recv);
if (recv)
{
recv->add_srtp_ctx(key, salt);
}
auto start = std::chrono::steady_clock::now();
uvgrtp::frame::rtp_frame* frame = nullptr;
if (recv)
{
while (std::chrono::steady_clock::now() - start < EXAMPLE_DURATION)
{
frame = recv->pull_frame(10);
if (frame)
{
process_frame(frame);
}
}
}
receiver_session->destroy_stream(recv);
ctx.destroy_session(receiver_session);
}
void process_frame(uvgrtp::frame::rtp_frame* frame)
{
std::string payload = std::string((char*)frame->payload, frame->payload_len);
EXPECT_NE(0, frame->payload_len);
(void)uvgrtp::frame::dealloc_frame(frame);
}

158
test/tests_rtcp.cpp Normal file
View File

@ -0,0 +1,158 @@
#include "lib.hh"
#include <gtest/gtest.h>
constexpr char LOCAL_INTERFACE[] = "127.0.0.1";
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
constexpr uint16_t PAYLOAD_LEN = 256;
constexpr uint16_t FRAME_RATE = 30;
constexpr uint32_t EXAMPLE_RUN_TIME_S = 15;
constexpr int SEND_TEST_PACKETS = FRAME_RATE * EXAMPLE_RUN_TIME_S;
constexpr int PACKET_INTERVAL_MS = 1000 / FRAME_RATE;
void receiver_hook(uvgrtp::frame::rtcp_receiver_report* frame);
void sender_hook(uvgrtp::frame::rtcp_sender_report* frame);
void wait_until_next_frame(std::chrono::steady_clock::time_point& start, int frame_index);
void cleanup(uvgrtp::context& ctx, uvgrtp::session* local_session, uvgrtp::session* remote_session,
uvgrtp::media_stream* send, uvgrtp::media_stream* receive);
TEST(RTCPTests, rtcp) {
std::cout << "Starting uvgRTP RTCP hook example" << std::endl;
// Creation of RTP stream. See sending example for more details
uvgrtp::context ctx;
uvgrtp::session* local_session = ctx.create_session(REMOTE_ADDRESS);
EXPECT_NE(nullptr, local_session);
uvgrtp::session* remote_session = ctx.create_session(LOCAL_INTERFACE);
EXPECT_NE(nullptr, remote_session);
int flags = RCE_RTCP;
uvgrtp::media_stream* local_stream = local_session->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_GENERIC, flags);
EXPECT_NE(nullptr, local_stream);
uvgrtp::media_stream* remote_stream = remote_session->create_stream(REMOTE_PORT, LOCAL_PORT,
RTP_FORMAT_GENERIC, flags);
EXPECT_NE(nullptr, remote_stream);
if (local_stream)
{
EXPECT_EQ(RTP_OK, local_stream->get_rtcp()->install_receiver_hook(receiver_hook));
}
if (remote_stream)
{
EXPECT_EQ(RTP_OK, remote_stream->get_rtcp()->install_sender_hook(sender_hook));
}
if (local_stream)
{
uint8_t buffer[PAYLOAD_LEN] = { 0 };
memset(buffer, 'a', PAYLOAD_LEN);
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < SEND_TEST_PACKETS; ++i)
{
EXPECT_EQ(RTP_OK, local_stream->push_frame((uint8_t*)buffer, PAYLOAD_LEN, RTP_NO_FLAGS));
wait_until_next_frame(start, i);
}
}
cleanup(ctx, local_session, remote_session, local_stream, remote_stream);
}
void receiver_hook(uvgrtp::frame::rtcp_receiver_report* frame)
{
std::cout << "RTCP receiver report! ----------" << std::endl;
for (auto& block : frame->report_blocks)
{
std::cout << "ssrc: " << block.ssrc << std::endl;
std::cout << "fraction: " << block.fraction << std::endl;
std::cout << "lost: " << block.lost << std::endl;
std::cout << "last_seq: " << block.last_seq << std::endl;
std::cout << "jitter: " << block.jitter << std::endl;
std::cout << "lsr: " << block.lsr << std::endl;
std::cout << "dlsr (ms): " << uvgrtp::clock::jiffies_to_ms(block.dlsr)
<< std::endl << std::endl;
}
/* RTCP frames can be deallocated using delete */
delete frame;
}
void sender_hook(uvgrtp::frame::rtcp_sender_report* frame)
{
std::cout << "RTCP sender report! ----------" << std::endl;
std::cout << "NTP msw: " << frame->sender_info.ntp_msw << std::endl;
std::cout << "NTP lsw: " << frame->sender_info.ntp_lsw << std::endl;
std::cout << "RTP timestamp: " << frame->sender_info.rtp_ts << std::endl;
std::cout << "packet count: " << frame->sender_info.pkt_cnt << std::endl;
std::cout << "byte count: " << frame->sender_info.byte_cnt << std::endl;
for (auto& block : frame->report_blocks)
{
std::cout << "ssrc: " << block.ssrc << std::endl;
std::cout << "fraction: " << block.fraction << std::endl;
std::cout << "lost: " << block.lost << std::endl;
std::cout << "last_seq: " << block.last_seq << std::endl;
std::cout << "jitter: " << block.jitter << std::endl;
std::cout << "lsr: " << block.lsr << std::endl;
std::cout << "dlsr (ms): " << uvgrtp::clock::jiffies_to_ms(block.dlsr)
<< std::endl << std::endl;
}
/* RTCP frames can be deallocated using delete */
delete frame;
}
void wait_until_next_frame(std::chrono::steady_clock::time_point& start, int frame_index)
{
// wait until it is time to send the next frame. Simulates a steady sending pace
// and included only for demostration purposes since you can use uvgRTP to send
// packets as fast as desired
auto time_since_start = std::chrono::steady_clock::now() - start;
auto next_frame_time = (frame_index + 1) * std::chrono::milliseconds(PACKET_INTERVAL_MS);
if (next_frame_time > time_since_start)
{
std::this_thread::sleep_for(next_frame_time - time_since_start);
}
}
void cleanup(uvgrtp::context& ctx, uvgrtp::session* local_session, uvgrtp::session* remote_session,
uvgrtp::media_stream* send, uvgrtp::media_stream* receive)
{
if (send)
{
local_session->destroy_stream(send);
}
if (receive)
{
remote_session->destroy_stream(receive);
}
if (local_session)
{
// Session must be destroyed manually
ctx.destroy_session(local_session);
}
if (remote_session)
{
// Session must be destroyed manually
ctx.destroy_session(remote_session);
}
}

91
test/tests_rtp.cpp Normal file
View File

@ -0,0 +1,91 @@
#include "lib.hh"
#include <gtest/gtest.h>
// parameters for this test. You can change these to suit your network environment
constexpr uint16_t LOCAL_PORT = 8890;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8888;
void rtp_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame);
void cleanup(uvgrtp::context& ctx, uvgrtp::session* sess, uvgrtp::media_stream* ms);
void process_rtp_frame(uvgrtp::frame::rtp_frame* frame);
// TODO: 1) Test only sending, 2) test sending with different configuration, 3) test receiving with different configurations, and
// 4) test sending and receiving within same test while checking frame size
TEST(RTPTests, rtp_hook)
{
uvgrtp::context ctx;
uvgrtp::session* sess = ctx.create_session(REMOTE_ADDRESS);
EXPECT_NE(nullptr, sess);
int flags = RTP_NO_FLAGS;
uvgrtp::media_stream* receiver = sess->create_stream(LOCAL_PORT, REMOTE_PORT, RTP_FORMAT_H265, flags);
EXPECT_NE(nullptr, receiver);
EXPECT_EQ(RTP_OK, receiver->install_receive_hook(nullptr, rtp_receive_hook));
std::this_thread::sleep_for(std::chrono::seconds(1)); // lets this example run for some time
cleanup(ctx, sess, receiver);
}
TEST(RTPTests, rtp_poll)
{
uvgrtp::context ctx;
uvgrtp::session* sess = ctx.create_session(REMOTE_ADDRESS);
EXPECT_NE(nullptr, sess);
int flags = RCE_NO_FLAGS;
uvgrtp::media_stream* receiver = sess->create_stream(LOCAL_PORT, REMOTE_PORT, RTP_FORMAT_H265, flags);
EXPECT_NE(nullptr, receiver);
uvgrtp::frame::rtp_frame* frame = nullptr;
auto start = std::chrono::steady_clock::now();
rtp_errno = RTP_OK;
while (std::chrono::steady_clock::now() - start < std::chrono::seconds(1))
{
frame = receiver->pull_frame(3);
EXPECT_EQ(RTP_OK, rtp_errno);
if (frame)
process_rtp_frame(frame);
}
sess->destroy_stream(receiver);
cleanup(ctx, sess, receiver);
}
void rtp_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame)
{
process_rtp_frame(frame);
}
void cleanup(uvgrtp::context& ctx, uvgrtp::session* sess, uvgrtp::media_stream* ms)
{
if (ms)
{
sess->destroy_stream(ms);
}
if (sess)
{
ctx.destroy_session(sess);
}
}
void process_rtp_frame(uvgrtp::frame::rtp_frame* frame)
{
EXPECT_NE(0, frame->payload_len);
(void)uvgrtp::frame::dealloc_frame(frame);
}

6
test/tests_version.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "lib.hh"
#include <gtest/gtest.h>
TEST(VersionTests, version) {
EXPECT_STRNE("", uvgrtp::get_version().c_str());
}