Add unit tests
This commit is contained in:
parent
2b14257c52
commit
14130bbf29
@ -58,21 +58,21 @@ target_link_libraries(playground PRIVATE spdlog::spdlog)
|
|||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
tcp_client_test
|
tcp_test
|
||||||
tests/tcp_client.cpp
|
tests/tcp.cpp
|
||||||
src/tcp.cpp
|
src/tcp.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
tcp_client_test
|
tcp_test
|
||||||
spdlog::spdlog
|
spdlog::spdlog
|
||||||
GTest::gtest_main
|
GTest::gtest_main
|
||||||
)
|
)
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
tcp_client_test
|
tcp_test
|
||||||
PRIVATE
|
PRIVATE
|
||||||
src
|
src
|
||||||
)
|
)
|
||||||
|
|
||||||
include(GoogleTest)
|
include(GoogleTest)
|
||||||
gtest_discover_tests(tcp_client_test)
|
gtest_discover_tests(tcp_test)
|
||||||
|
|
||||||
|
|||||||
22
src/tcp.cpp
22
src/tcp.cpp
@ -63,6 +63,11 @@ NonBlockingServer::NonBlockingServer(ServerSettings settings)
|
|||||||
: m_settings{settings}, m_serverAddress(get_local_address(settings.port)) {
|
: m_settings{settings}, m_serverAddress(get_local_address(settings.port)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonBlockingServer::~NonBlockingServer() {
|
||||||
|
close_client_socket();
|
||||||
|
close_server_socket();
|
||||||
|
}
|
||||||
|
|
||||||
std::expected<void, int> NonBlockingServer::start_listening() {
|
std::expected<void, int> NonBlockingServer::start_listening() {
|
||||||
/// Create socket
|
/// Create socket
|
||||||
|
|
||||||
@ -254,6 +259,10 @@ void NonBlockingServer::close_client_socket() {
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
NonBlockingClient::~NonBlockingClient() {
|
||||||
|
close_socket();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, int>
|
[[nodiscard]] std::expected<void, int>
|
||||||
NonBlockingClient::connect(HostString host, uint16_t port) {
|
NonBlockingClient::connect(HostString host, uint16_t port) {
|
||||||
/// Resolve host
|
/// Resolve host
|
||||||
@ -280,14 +289,14 @@ NonBlockingClient::connect(HostString host, uint16_t port) {
|
|||||||
return std::unexpected{errno};
|
return std::unexpected{errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
m_startedNewConAttemt = true;
|
m_startedNewConAttempt = true;
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<int, int>
|
[[nodiscard]] std::expected<int, int>
|
||||||
NonBlockingClient::get_last_connection_status() {
|
NonBlockingClient::get_last_connection_status() {
|
||||||
if (!m_startedNewConAttemt) return m_lastKnownConStatus;
|
if (!m_startedNewConAttempt) return m_lastKnownConStatus;
|
||||||
|
|
||||||
/// Check if connect operation has been completed
|
/// Check if connect operation has been completed
|
||||||
|
|
||||||
@ -295,7 +304,7 @@ NonBlockingClient::get_last_connection_status() {
|
|||||||
if (!pendingOpRes) return std::unexpected{pendingOpRes.error()};
|
if (!pendingOpRes) return std::unexpected{pendingOpRes.error()};
|
||||||
if (!pendingOpRes.value()) return EINPROGRESS;
|
if (!pendingOpRes.value()) return EINPROGRESS;
|
||||||
|
|
||||||
m_startedNewConAttemt = false;
|
m_startedNewConAttempt = false;
|
||||||
|
|
||||||
/// Check for connection errors
|
/// Check for connection errors
|
||||||
|
|
||||||
@ -317,6 +326,7 @@ NonBlockingClient::get_last_connection_status() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NonBlockingClient::disconnect() {
|
void NonBlockingClient::disconnect() {
|
||||||
|
m_lastKnownConStatus = ENOTCONN;
|
||||||
close_socket();
|
close_socket();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +335,7 @@ NonBlockingClient::send(std::span<const std::byte> data) {
|
|||||||
// TODO: Do we need this?
|
// TODO: Do we need this?
|
||||||
auto conRes = get_last_connection_status();
|
auto conRes = get_last_connection_status();
|
||||||
if (!conRes) return std::unexpected{conRes.error()};
|
if (!conRes) return std::unexpected{conRes.error()};
|
||||||
if (conRes != EISCONN) {
|
if (conRes.value() != EISCONN) {
|
||||||
spdlog::error("tcp::NonBlockingClient: Socket not connected");
|
spdlog::error("tcp::NonBlockingClient: Socket not connected");
|
||||||
return std::unexpected{ENOTCONN};
|
return std::unexpected{ENOTCONN};
|
||||||
}
|
}
|
||||||
@ -345,7 +355,7 @@ NonBlockingClient::send(std::span<const std::byte> data) {
|
|||||||
bool NonBlockingClient::data_available() {
|
bool NonBlockingClient::data_available() {
|
||||||
auto conRes = get_last_connection_status();
|
auto conRes = get_last_connection_status();
|
||||||
if (!conRes) return false;
|
if (!conRes) return false;
|
||||||
if (conRes != EISCONN) return false;
|
if (conRes.value() != EISCONN) return false;
|
||||||
|
|
||||||
int ret = poll(&m_pfdIn, 1, 0);
|
int ret = poll(&m_pfdIn, 1, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -354,7 +364,6 @@ bool NonBlockingClient::data_available() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (m_pfdIn.revents & POLLIN);
|
return (m_pfdIn.revents & POLLIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,6 +434,7 @@ NonBlockingClient::new_error_codes_available() {
|
|||||||
return std::unexpected{errno};
|
return std::unexpected{errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ret == 0 means the operation timed out, i.e., no new events
|
||||||
return (ret != 0);
|
return (ret != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
src/tcp.hpp
13
src/tcp.hpp
@ -39,13 +39,13 @@ struct ServerSettings {
|
|||||||
class NonBlockingServer {
|
class NonBlockingServer {
|
||||||
public:
|
public:
|
||||||
NonBlockingServer(ServerSettings settings);
|
NonBlockingServer(ServerSettings settings);
|
||||||
|
~NonBlockingServer();
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, int> start_listening();
|
[[nodiscard]] std::expected<void, int> start_listening();
|
||||||
|
|
||||||
bool next_client_available();
|
bool next_client_available();
|
||||||
[[nodiscard]] std::expected<void, int> accept_next_client();
|
[[nodiscard]] std::expected<void, int> accept_next_client();
|
||||||
void disconnect();
|
void disconnect();
|
||||||
// bool socket_closed() const;
|
|
||||||
|
|
||||||
[[nodiscard]] std::expected<int, int>
|
[[nodiscard]] std::expected<int, int>
|
||||||
send(std::span<const std::byte> data) const;
|
send(std::span<const std::byte> data) const;
|
||||||
@ -86,15 +86,16 @@ private:
|
|||||||
|
|
||||||
class NonBlockingClient {
|
class NonBlockingClient {
|
||||||
public:
|
public:
|
||||||
|
~NonBlockingClient();
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
[[nodiscard]] std::expected<void, int>
|
[[nodiscard]] std::expected<void, int>
|
||||||
connect(HostString host, uint16_t port);
|
connect(HostString host, uint16_t port);
|
||||||
// clang-format off
|
// clang-format on
|
||||||
|
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
[[nodiscard]] std::expected<int, int>
|
[[nodiscard]] std::expected<int, int> get_last_connection_status();
|
||||||
get_last_connection_status();
|
|
||||||
|
|
||||||
bool data_available();
|
bool data_available();
|
||||||
|
|
||||||
@ -106,8 +107,8 @@ private:
|
|||||||
pollfd m_pfdOut = {.fd = -1, .events = POLLOUT};
|
pollfd m_pfdOut = {.fd = -1, .events = POLLOUT};
|
||||||
pollfd m_pfdIn = {.fd = -1, .events = POLLIN};
|
pollfd m_pfdIn = {.fd = -1, .events = POLLIN};
|
||||||
|
|
||||||
bool m_startedNewConAttemt = false;
|
bool m_startedNewConAttempt = false;
|
||||||
int m_lastKnownConStatus = 0;
|
int m_lastKnownConStatus = ENOTCONN;
|
||||||
|
|
||||||
void close_socket();
|
void close_socket();
|
||||||
|
|
||||||
|
|||||||
168
tests/tcp.cpp
Normal file
168
tests/tcp.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#include "tcp.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TcpServer, Accept) {
|
||||||
|
tcp::NonBlockingServer server{{.port = 1234}};
|
||||||
|
tcp::NonBlockingClient client;
|
||||||
|
|
||||||
|
auto lisRes = server.start_listening();
|
||||||
|
|
||||||
|
EXPECT_FALSE(server.next_client_available());
|
||||||
|
|
||||||
|
auto conRes = client.connect({"localhost"}, 1234);
|
||||||
|
|
||||||
|
EXPECT_TRUE(server.next_client_available());
|
||||||
|
|
||||||
|
auto accRes = server.accept_next_client();
|
||||||
|
|
||||||
|
EXPECT_FALSE(server.next_client_available());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TcpClient, Connect) {
|
||||||
|
tcp::NonBlockingServer server{{.port = 1234}};
|
||||||
|
tcp::NonBlockingClient client;
|
||||||
|
|
||||||
|
/// Default connection status, i.e., before attempting connection
|
||||||
|
|
||||||
|
auto statRes1 = client.get_last_connection_status();
|
||||||
|
if (!statRes1) return;
|
||||||
|
EXPECT_EQ(statRes1.value(), ENOTCONN);
|
||||||
|
|
||||||
|
/// Connection status when connection is not acknowledged
|
||||||
|
|
||||||
|
auto conRes = client.connect({"localhost"}, 1234);
|
||||||
|
|
||||||
|
auto statRes2 = client.get_last_connection_status();
|
||||||
|
if (!statRes2) return;
|
||||||
|
EXPECT_EQ(statRes2.value(), ECONNREFUSED);
|
||||||
|
|
||||||
|
/// Connection status when connection is acknowledged
|
||||||
|
|
||||||
|
auto lisRes = server.start_listening();
|
||||||
|
|
||||||
|
auto conRes2 = client.connect({"localhost"}, 1234);
|
||||||
|
|
||||||
|
auto statRes3 = client.get_last_connection_status();
|
||||||
|
if (!statRes3) return;
|
||||||
|
EXPECT_EQ(statRes3.value(), EISCONN);
|
||||||
|
|
||||||
|
/// Connection status changes when reattempting connection
|
||||||
|
|
||||||
|
auto conRes3 = client.connect({"localhost"}, 12345);
|
||||||
|
|
||||||
|
auto statRes4 = client.get_last_connection_status();
|
||||||
|
if (!statRes4) return;
|
||||||
|
EXPECT_EQ(statRes4.value(), ECONNREFUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TcpClient, Reonnect) {
|
||||||
|
tcp::NonBlockingServer server1{{.port = 1234}};
|
||||||
|
tcp::NonBlockingServer server2{{.port = 2345}};
|
||||||
|
tcp::NonBlockingClient client;
|
||||||
|
|
||||||
|
auto lisRes1 = server1.start_listening();
|
||||||
|
auto lisRes2 = server2.start_listening();
|
||||||
|
|
||||||
|
auto conRes1 = client.connect({"localhost"}, 1234);
|
||||||
|
|
||||||
|
auto statRes1 = client.get_last_connection_status();
|
||||||
|
if (!statRes1) return;
|
||||||
|
EXPECT_EQ(statRes1.value(), EISCONN);
|
||||||
|
|
||||||
|
auto conRes2 = client.connect({"localhost"}, 2345);
|
||||||
|
|
||||||
|
auto statRes2 = client.get_last_connection_status();
|
||||||
|
if (!statRes2) return;
|
||||||
|
EXPECT_EQ(statRes2.value(), EISCONN);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TcpClient, DataAvailable) {
|
||||||
|
tcp::NonBlockingServer server{{.port = 1234}};
|
||||||
|
tcp::NonBlockingClient client;
|
||||||
|
|
||||||
|
EXPECT_FALSE(client.data_available());
|
||||||
|
|
||||||
|
auto lisRes = server.start_listening();
|
||||||
|
auto conRes = client.connect({"localhost"}, 1234);
|
||||||
|
auto accRes = server.accept_next_client();
|
||||||
|
|
||||||
|
EXPECT_EQ(client.get_last_connection_status().value(), EISCONN);
|
||||||
|
EXPECT_FALSE(client.data_available());
|
||||||
|
|
||||||
|
std::array<char, 14> txMsg = {"Hello, World!"};
|
||||||
|
auto sendRes = server.send(std::as_bytes(std::span(txMsg)));
|
||||||
|
|
||||||
|
EXPECT_EQ(sendRes.value(), txMsg.size());
|
||||||
|
EXPECT_TRUE(client.data_available());
|
||||||
|
|
||||||
|
std::array<char, 1024> rxBuffer;
|
||||||
|
auto recvRes = client.recv(std::as_writable_bytes(std::span(rxBuffer)));
|
||||||
|
EXPECT_EQ(recvRes.value(), txMsg.size());
|
||||||
|
|
||||||
|
EXPECT_FALSE(client.data_available());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TcpServer, DataAvailable) {
|
||||||
|
tcp::NonBlockingServer server{{.port = 1234}};
|
||||||
|
tcp::NonBlockingClient client;
|
||||||
|
|
||||||
|
EXPECT_FALSE(server.data_available());
|
||||||
|
|
||||||
|
auto lisRes = server.start_listening();
|
||||||
|
auto conRes = client.connect({"localhost"}, 1234);
|
||||||
|
auto accRes = server.accept_next_client();
|
||||||
|
|
||||||
|
EXPECT_FALSE(server.data_available());
|
||||||
|
|
||||||
|
std::array<char, 14> txMsg = {"Hello, World!"};
|
||||||
|
auto sendRes = client.send(std::as_bytes(std::span(txMsg)));
|
||||||
|
EXPECT_EQ(sendRes.value(), txMsg.size());
|
||||||
|
EXPECT_TRUE(server.data_available());
|
||||||
|
|
||||||
|
std::array<char, 1024> rxBuffer;
|
||||||
|
auto recvRes = server.recv(std::as_writable_bytes(std::span(rxBuffer)));
|
||||||
|
EXPECT_EQ(recvRes.value(), txMsg.size());
|
||||||
|
EXPECT_FALSE(server.data_available());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TcpClientServer, ClientSendServerReceive) {
|
||||||
|
tcp::NonBlockingServer server{{.port = 1234}};
|
||||||
|
tcp::NonBlockingClient client;
|
||||||
|
|
||||||
|
auto lisRes = server.start_listening();
|
||||||
|
auto conRes = client.connect({"localhost"}, 1234);
|
||||||
|
auto accRes = server.accept_next_client();
|
||||||
|
|
||||||
|
std::array<char, 14> txMsg = {"Hello, World!"};
|
||||||
|
auto sendRes = client.send(std::as_bytes(std::span(txMsg)));
|
||||||
|
|
||||||
|
EXPECT_EQ(sendRes.value(), txMsg.size());
|
||||||
|
|
||||||
|
std::array<char, 1024> rxBuffer;
|
||||||
|
auto recvRes = server.recv(std::as_writable_bytes(std::span(rxBuffer)));
|
||||||
|
|
||||||
|
EXPECT_EQ(recvRes.value(), txMsg.size());
|
||||||
|
EXPECT_EQ(std::strcmp(txMsg.data(), rxBuffer.data()), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TcpClientServer, ClientReceiveServerSend) {
|
||||||
|
tcp::NonBlockingServer server{{.port = 1234}};
|
||||||
|
tcp::NonBlockingClient client;
|
||||||
|
|
||||||
|
auto lisRes = server.start_listening();
|
||||||
|
auto conRes = client.connect({"localhost"}, 1234);
|
||||||
|
auto accRes = server.accept_next_client();
|
||||||
|
|
||||||
|
std::array<char, 14> txMsg = {"Hello, World!"};
|
||||||
|
auto sendRes = server.send(std::as_bytes(std::span(txMsg)));
|
||||||
|
|
||||||
|
EXPECT_EQ(sendRes.value(), txMsg.size());
|
||||||
|
|
||||||
|
std::array<char, 1024> rxBuffer;
|
||||||
|
auto recvRes = client.recv(std::as_writable_bytes(std::span(rxBuffer)));
|
||||||
|
|
||||||
|
EXPECT_EQ(recvRes.value(), txMsg.size());
|
||||||
|
EXPECT_EQ(std::strcmp(txMsg.data(), rxBuffer.data()), 0);
|
||||||
|
}
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#include "tcp.hpp"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
TEST(TcpClient, Connect) {
|
|
||||||
tcp::NonBlockingClient client;
|
|
||||||
}
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user