#include "gtest/gtest.h" // Google Test Framework #include "test_utils.h" // Helpers for tests // Test subject (SUT) #include "TCPClient.h" #include "TCPServer.h" #include "TCPSSLServer.h" #include "TCPSSLClient.h" #define PRINT_LOG [](const std::string& strLogMsg) { std::cout << strLogMsg << std::endl; } // Test parameters extern bool TCP_TEST_ENABLED; extern bool SECURE_TCP_TEST_ENABLED; extern bool HTTP_PROXY_TEST_ENABLED; extern std::string TCP_SERVER_PORT; extern std::string SECURE_TCP_SERVER_PORT; extern std::string CERT_AUTH_FILE; extern std::string SSL_CERT_FILE; extern std::string SSL_KEY_FILE; extern std::string PROXY_SERVER; extern std::string PROXY_SERVER_FAKE; extern std::mutex g_mtxConsoleMutex; namespace { // fixture for TCP tests class TCPTest : public ::testing::Test { protected: std::unique_ptr m_pTCPClient; std::unique_ptr m_pTCPServer; TCPTest() : m_pTCPClient(nullptr), m_pTCPServer(nullptr) { } virtual ~TCPTest() { } virtual void SetUp() { m_pTCPClient.reset(new CTCPClient(PRINT_LOG)); } virtual void TearDown() { if (m_pTCPClient.get() != nullptr) { m_pTCPClient.reset(); } if (m_pTCPServer.get() != nullptr) { m_pTCPServer.reset(); } } }; #ifdef OPENSSL // fixture for TCP SSL tests class SSLTCPTest : public ::testing::Test { protected: std::unique_ptr m_pSSLTCPClient; std::unique_ptr m_pSSLTCPServer; SSLTCPTest() : m_pSSLTCPClient(nullptr), m_pSSLTCPServer(nullptr) { } virtual ~SSLTCPTest() { } virtual void SetUp() { m_pSSLTCPClient.reset(new CTCPSSLClient(PRINT_LOG)); } virtual void TearDown() { if (m_pSSLTCPClient.get() != nullptr) { m_pSSLTCPClient.reset(); } if (m_pSSLTCPServer.get() != nullptr) { m_pSSLTCPServer.reset(); } } }; #endif // Unit tests /* TEST_F(TCPTest, TestServer) { const std::string strSendData = "Hello World !"; char szRcvBuffer[14] = {}; ASocket::Socket ConnectedClient; ASSERT_NO_THROW(m_pTCPServer.reset(new CTCPServer(PRINT_LOG, "6669"))); ASSERT_TRUE(m_pTCPServer->Listen(ConnectedClient)); int iCount = 0; while (iCount++ < 3) { EXPECT_GT(m_pTCPServer->Receive(ConnectedClient, szRcvBuffer, 13), 0); EXPECT_TRUE(m_pTCPServer->Send(ConnectedClient, strSendData)); EXPECT_EQ(strSendData, szRcvBuffer); bzero(szRcvBuffer, sizeof(szRcvBuffer)); } // disconnect EXPECT_TRUE(m_pTCPServer->Disconnect(ConnectedClient)); } */ /* TEST_F(TCPTest, TestClient) { const std::string strSendData = "Hello World !"; char szRcvBuffer[14] = {}; ASSERT_TRUE(m_pTCPClient->Connect("localhost", "6669")); // perform 3 checks unsigned uCount = 0; while (uCount++ < 3) { EXPECT_GT(m_pTCPClient->Receive(szRcvBuffer, 13), 0); EXPECT_TRUE(m_pTCPClient->Send(strSendData)); EXPECT_EQ(strSendData, szRcvBuffer); memset(szRcvBuffer, '\0', 14); } // disconnect EXPECT_TRUE(m_pTCPClient->Disconnect()); } */ TEST_F(TCPTest, TestLoopbackTenMegaBytes) { if (TCP_TEST_ENABLED) { srand(static_cast(time(nullptr))); const size_t tenMeg = 10*1024*1024; std::vector TenMbData(tenMeg); std::vector RcvBuffer(tenMeg); std::generate (TenMbData.begin(), TenMbData.end(), []{ return (std::rand() % 256); }); ASocket::Socket ConnectedClient; ASSERT_NO_THROW(m_pTCPServer.reset(new CTCPServer(PRINT_LOG, TCP_SERVER_PORT))); // Not always starts a new thread, std::launch::async must be passed to force it. std::future futListen = std::async(std::launch::async, [&] { return m_pTCPServer->Listen(ConnectedClient); }); // give time to let the server object reach the accept instruction. SleepMs(500); ASSERT_TRUE(m_pTCPClient->Connect("localhost", "6669")); ASSERT_TRUE(futListen.get()); auto ClientReceive = [&]() { return m_pTCPClient->Receive(RcvBuffer.data(), tenMeg); }; // launch the receive in another thread to prevent server from // hanging when sending "many" bytes. std::future futClientReceive = std::async(std::launch::async, ClientReceive); ASSERT_FALSE(ConnectedClient == INVALID_SOCKET); // server -> client EXPECT_TRUE(m_pTCPServer->Send(ConnectedClient, TenMbData)); int nRcvdBytes = futClientReceive.get(); EXPECT_EQ(nRcvdBytes, tenMeg); EXPECT_TRUE(std::equal(TenMbData.begin(), TenMbData.end(), RcvBuffer.begin())); std::fill(RcvBuffer.begin(), RcvBuffer.end(), 0); // client -> server auto ServerReceive = [&]() { return m_pTCPServer->Receive(ConnectedClient, RcvBuffer.data(), tenMeg); }; std::future futServerReceive = std::async(std::launch::async, ServerReceive); EXPECT_TRUE(m_pTCPClient->Send(TenMbData)); nRcvdBytes = futServerReceive.get(); EXPECT_EQ(nRcvdBytes, tenMeg); EXPECT_TRUE(std::equal(TenMbData.begin(), TenMbData.end(), RcvBuffer.begin())); // disconnect EXPECT_TRUE(m_pTCPClient->Disconnect()); //EXPECT_TRUE(m_pTCPServer->Disconnect(ConnectedClient)); // OK tested } else std::cout << "TCP tests are disabled !" << std::endl; } TEST_F(TCPTest, TestPollSocketForReceivedBytes) { if (TCP_TEST_ENABLED) { srand(static_cast(time(nullptr))); const size_t tenMeg = 10 * 1024 * 1024; std::vector TenMbData(tenMeg); std::vector RcvBuffer(tenMeg); std::generate(TenMbData.begin(), TenMbData.end(), [] { return (std::rand() % 256); }); ASocket::Socket ConnectedClient; ASSERT_NO_THROW(m_pTCPServer.reset(new CTCPServer(PRINT_LOG, TCP_SERVER_PORT))); // Not always starts a new thread, std::launch::async must be passed to force it. std::future futListen = std::async(std::launch::async, [&] { return m_pTCPServer->Listen(ConnectedClient); }); // give time to let the server object reach the accept instruction. SleepMs(500); ASSERT_TRUE(m_pTCPClient->Connect("localhost", "6669")); ASSERT_TRUE(futListen.get()); auto ClientReceive = [&]() -> int { const int chunkSize = 1024 * 1024; // 1 MB int readBytes = 0; int timeoutCount = 0; while (timeoutCount < 20) { int ret = ASocket::SelectSocket(m_pTCPClient->GetSocketDescriptor(), 300); if (ret > 0) { int readCount = m_pTCPClient->Receive(RcvBuffer.data() + readBytes, chunkSize); if (readCount == 0) break; readBytes += readCount; } else { timeoutCount += 1; } if (readBytes >= tenMeg) break; } return readBytes; }; // launch the receive in another thread to prevent server from // hanging when sending "many" bytes. std::future futClientReceive = std::async(std::launch::async, ClientReceive); ASSERT_FALSE(ConnectedClient == INVALID_SOCKET); // server -> client EXPECT_TRUE(m_pTCPServer->Send(ConnectedClient, TenMbData)); int nRcvdBytes = futClientReceive.get(); EXPECT_EQ(nRcvdBytes, tenMeg); EXPECT_TRUE(std::equal(TenMbData.begin(), TenMbData.end(), RcvBuffer.begin())); std::fill(RcvBuffer.begin(), RcvBuffer.end(), 0); // client -> server auto ServerReceive = [&]() -> int { const int chunkSize = 1024 * 1024; // 1 MB int readBytes = 0; int timeoutCount = 0; while (timeoutCount < 20) { int ret = ASocket::SelectSocket(ConnectedClient, 300); if (ret > 0) { int readCount = m_pTCPServer->Receive(ConnectedClient, RcvBuffer.data() + readBytes, chunkSize); if (readCount == 0) break; readBytes += readCount; } else { timeoutCount += 1; } if (readBytes >= tenMeg) break; } return readBytes; }; std::future futServerReceive = std::async(std::launch::async, ServerReceive); EXPECT_TRUE(m_pTCPClient->Send(TenMbData)); nRcvdBytes = futServerReceive.get(); EXPECT_EQ(nRcvdBytes, tenMeg); EXPECT_TRUE(std::equal(TenMbData.begin(), TenMbData.end(), RcvBuffer.begin())); // disconnect EXPECT_TRUE(m_pTCPClient->Disconnect()); } else std::cout << "TCP tests are disabled !" << std::endl; } TEST_F(TCPTest, TestPollSocketTimeout) { if (TCP_TEST_ENABLED) { ASocket::Socket ConnectedClient; ASSERT_NO_THROW(m_pTCPServer.reset(new CTCPServer(PRINT_LOG, TCP_SERVER_PORT))); // Not always starts a new thread, std::launch::async must be passed to force it. std::future futListen = std::async(std::launch::async, [&] { return m_pTCPServer->Listen(ConnectedClient); }); // give time to let the server object reach the accept instruction. SleepMs(100); ASSERT_TRUE(m_pTCPClient->Connect("localhost", "6669")); ASSERT_TRUE(futListen.get()); auto ClientReceive = [&](){ int timeoutCount = 0; while (timeoutCount < 10) { int ret = ASocket::SelectSocket(m_pTCPClient->GetSocketDescriptor(), 50); ASSERT_EQ(ret, 0); timeoutCount += 1; } }; // launch the receive in another thread to prevent server from // hanging when sending "many" bytes. std::future futClientReceive = std::async(std::launch::async, ClientReceive); ASSERT_FALSE(ConnectedClient == INVALID_SOCKET); // do not send anything from the server to the client futClientReceive.get(); // client -> server auto ServerReceive = [&](){ int timeoutCount = 0; while (timeoutCount < 10) { int ret = ASocket::SelectSocket(ConnectedClient, 50); ASSERT_EQ(ret, 0); timeoutCount += 1; } }; std::future futServerReceive = std::async(std::launch::async, ServerReceive); // do not send anything from the client to the server futServerReceive.get(); // disconnect EXPECT_TRUE(m_pTCPClient->Disconnect()); } else std::cout << "TCP tests are disabled !" << std::endl; } TEST_F(TCPTest, TestReceivingTimeout) { if (TCP_TEST_ENABLED) { srand(static_cast(time(nullptr))); const size_t tenMeg = 10 * 1024 * 1024; std::vector TenMbData(tenMeg); std::vector RcvBuffer(tenMeg); std::generate(TenMbData.begin(), TenMbData.end(), [] { return (std::rand() % 256); }); ASocket::Socket ConnectedClient; ASSERT_NO_THROW(m_pTCPServer.reset(new CTCPServer(PRINT_LOG, TCP_SERVER_PORT))); // Not always starts a new thread, std::launch::async must be passed to force it. std::future futListen = std::async(std::launch::async, [&] { return m_pTCPServer->Listen(ConnectedClient); }); // give time to let the server object reach the accept instruction. SleepMs(200); ASSERT_TRUE(m_pTCPClient->Connect("localhost", "6669")); ASSERT_TRUE(futListen.get()); ASSERT_TRUE(m_pTCPClient->SetRcvTimeout(250)); auto ClientReceive = [&]() { return m_pTCPClient->Receive(RcvBuffer.data(), tenMeg); }; // launch the receive in another thread to prevent server from // hanging when sending "many" bytes. std::future futClientReceive = std::async(std::launch::async, ClientReceive); ASSERT_FALSE(ConnectedClient == INVALID_SOCKET); // server -> client //EXPECT_TRUE(m_pTCPServer->Send(ConnectedClient, TenMbData)); // send nothing int nRcvdBytes = futClientReceive.get(); EXPECT_EQ(nRcvdBytes, -1); m_pTCPServer->SetRcvTimeout(ConnectedClient, 250); // client -> server auto ServerReceive = [&]() { return m_pTCPServer->Receive(ConnectedClient, RcvBuffer.data(), tenMeg); }; std::future futServerReceive = std::async(std::launch::async, ServerReceive); //EXPECT_TRUE(m_pTCPClient->Send(TenMbData)); nRcvdBytes = futServerReceive.get(); EXPECT_EQ(nRcvdBytes, -1); // disconnect EXPECT_TRUE(m_pTCPClient->Disconnect()); //EXPECT_TRUE(m_pTCPServer->Disconnect(ConnectedClient)); // OK tested } else std::cout << "TCP tests are disabled !" << std::endl; } TEST_F(TCPTest, TestSuccessfulListenWithTimeout) { if (TCP_TEST_ENABLED) { srand(static_cast(time(nullptr))); const size_t oneMeg = 1024*1024; std::vector OneMbData(oneMeg); std::vector RcvBuffer(oneMeg); std::generate (OneMbData.begin(), OneMbData.end(), []{ return (std::rand() % 256); }); ASocket::Socket ConnectedClient; ASSERT_NO_THROW(m_pTCPServer.reset(new CTCPServer(PRINT_LOG, TCP_SERVER_PORT))); #ifdef WINDOWS // Not always starts a new thread, std::launch::async must be passed to force it. std::future futListen = std::async(std::launch::async, [&] { return m_pTCPServer->Listen(ConnectedClient, 1000); }); #else auto ListenTask = [&] { return m_pTCPServer->Listen(ConnectedClient, 1000); }; // 1 sec max wait std::packaged_task< bool(void) > packageListen { ListenTask }; std::future futListen = packageListen.get_future(); std::thread ListenThread { std::move(packageListen) }; // pack. task is not copyable #endif // client side // give time to let the server object reach the accept instruction. SleepMs(100); ASSERT_TRUE(m_pTCPClient->Connect("localhost", "6669")); #ifdef WINDOWS ASSERT_TRUE(futListen.get()); #else /* with std::thread we can't easily get the result of Listen * unlike std::async/promise/packaged_task */ ASSERT_TRUE(futListen.get()); ListenThread.join(); #endif ASSERT_FALSE(ConnectedClient == INVALID_SOCKET); // server -> client EXPECT_TRUE(m_pTCPServer->Send(ConnectedClient, OneMbData)); EXPECT_GT(m_pTCPClient->Receive(RcvBuffer.data(), oneMeg), 0); EXPECT_TRUE(std::equal(OneMbData.begin(), OneMbData.end(), RcvBuffer.begin())); std::fill(RcvBuffer.begin(), RcvBuffer.end(), 0); // client -> server EXPECT_TRUE(m_pTCPClient->Send(OneMbData)); EXPECT_GT(m_pTCPServer->Receive(ConnectedClient, RcvBuffer.data(), oneMeg), 0); EXPECT_TRUE(std::equal(OneMbData.begin(), OneMbData.end(), RcvBuffer.begin())); // disconnect EXPECT_TRUE(m_pTCPClient->Disconnect()); //EXPECT_TRUE(m_pTCPServer->Disconnect(ConnectedClient)); // OK tested } else std::cout << "TCP tests are disabled !" << std::endl; } TEST_F(TCPTest, TestListenFailureWithTimeout) { if (TCP_TEST_ENABLED) { ASocket::Socket ConnectedClient; ASSERT_NO_THROW(m_pTCPServer.reset(new CTCPServer(PRINT_LOG, TCP_SERVER_PORT))); ASSERT_FALSE(m_pTCPServer->Listen(ConnectedClient, 3000)); // 3 sec max wait ASSERT_TRUE(ConnectedClient == INVALID_SOCKET); } else std::cout << "TCP tests are disabled !" << std::endl; } #ifdef OPENSSL /* TEST_F(SSLTCPTest, TestServer) { const std::string strSendData = "Hello World !"; char szRcvBuffer[14] = {}; ASecureSocket::SSLSocket ConnectedClient; ASSERT_NO_THROW(m_pSSLTCPServer.reset(new CTCPSSLServer(PRINT_LOG, "4242"))); m_pSSLTCPServer->SetSSLCertFile(SSL_CERT_FILE); m_pSSLTCPServer->SetSSLKeyFile(SSL_KEY_FILE); ASSERT_TRUE(m_pSSLTCPServer->Listen(ConnectedClient)); int iCount = 0; while (iCount++ < 3) { EXPECT_GT(m_pSSLTCPServer->Receive(ConnectedClient, szRcvBuffer, 13), 0); EXPECT_TRUE(m_pSSLTCPServer->Send(ConnectedClient, strSendData)); EXPECT_EQ(strSendData, szRcvBuffer); memset(szRcvBuffer, '\0', sizeof(szRcvBuffer)); } // disconnect EXPECT_TRUE(m_pSSLTCPServer->Disconnect(ConnectedClient)); } */ TEST_F(SSLTCPTest, TestLoopbackTenMegabytes) { if (SECURE_TCP_TEST_ENABLED) { srand(static_cast(time(nullptr))); const size_t tenMeg = 10*1024*1024; std::vector TenMbData(tenMeg); std::vector RcvBuffer(tenMeg); std::generate (TenMbData.begin(), TenMbData.end(), []{ return (std::rand() % 256); }); ASecureSocket::SSLSocket ConnectedClient; ASSERT_NO_THROW(m_pSSLTCPServer.reset(new CTCPSSLServer(PRINT_LOG, SECURE_TCP_SERVER_PORT))); m_pSSLTCPServer->SetSSLCertFile(SSL_CERT_FILE); m_pSSLTCPServer->SetSSLKeyFile(SSL_KEY_FILE); //m_pSSLTCPServer->SetSSLCerthAuth(CERT_AUTH_FILE); // not mandatory #ifdef WINDOWS // Not always starts a new thread, std::launch::async must be passed to force it. std::future futConnect = std::async(std::launch::async, [&]() -> bool { // give time to let the server object reach the accept instruction. SleepMs(1000); //m_pSSLTCPClient->SetSSLCerthAuth(CERT_AUTH_FILE); // not mandatory //m_pSSLTCPClient->SetSSLKeyFile(SSL_KEY_FILE); // not mandatory return m_pSSLTCPClient->Connect("localhost", SECURE_TCP_SERVER_PORT); }); #else auto ConnectTask = [&]() -> bool { // give time to let the server object reach the accept instruction. std::cout << "** Connect task : delay 2 seconds\n"; SleepMs(2000); std::cout << "** Connect task : begin connect\n"; //m_pSSLTCPClient->SetSSLKeyFile(SSL_KEY_FILE); // not mandatory //m_pSSLTCPClient->SetSSLCerthAuth(CERT_AUTH_FILE); // not mandatory bool bRet = m_pSSLTCPClient->Connect("localhost", "4242"); std::cout << "** Connect task : end connect\n"; return bRet; }; std::packaged_task< bool(void) > packageConnect { ConnectTask }; std::future futConnect = packageConnect.get_future(); std::thread ConnectThread { std::move(packageConnect) }; // pack. task is not copyable #endif m_pSSLTCPServer->Listen(ConnectedClient); #ifdef WINDOWS ASSERT_TRUE(futConnect.get()); #else /* with std::thread we can't easily get the result of Listen * contrary to std::async/promise/packaged_task */ ASSERT_TRUE(futConnect.get()); ConnectThread.join(); #endif auto ClientReceive = [&]() { return m_pSSLTCPClient->Receive(RcvBuffer.data(), tenMeg); }; // launch the receive in another thread to prevent server from // hanging when sending "many" bytes. std::future futClientReceive = std::async(std::launch::async, ClientReceive); ASSERT_FALSE(ConnectedClient.m_pSSL == nullptr); ASSERT_FALSE(ConnectedClient.m_pCTXSSL == nullptr); ASSERT_FALSE(ConnectedClient.m_SockFd == INVALID_SOCKET); // server -> client EXPECT_TRUE(m_pSSLTCPServer->Send(ConnectedClient, TenMbData)); int nRcvdBytes = futClientReceive.get(); EXPECT_EQ(nRcvdBytes, tenMeg); EXPECT_TRUE(std::equal(TenMbData.begin(), TenMbData.end(), RcvBuffer.begin())); std::fill(RcvBuffer.begin(), RcvBuffer.end(), 0); // client -> server auto ServerReceive = [&]() { return m_pSSLTCPServer->Receive(ConnectedClient, RcvBuffer.data(), tenMeg); }; std::future futServerReceive = std::async(std::launch::async, ServerReceive); EXPECT_TRUE(m_pSSLTCPClient->Send(TenMbData)); nRcvdBytes = futServerReceive.get(); EXPECT_EQ(nRcvdBytes, tenMeg); EXPECT_TRUE(std::equal(TenMbData.begin(), TenMbData.end(), RcvBuffer.begin())); // disconnect EXPECT_TRUE(m_pSSLTCPClient->Disconnect()); EXPECT_TRUE(m_pSSLTCPServer->Disconnect(ConnectedClient)); } else std::cout << "SECURE TCP tests are disabled !" << std::endl; } #endif } // namespace int main(int argc, char **argv) { if (argc > 1 && GlobalTestInit(argv[1])) // loading test parameters from the INI file... { ::testing::InitGoogleTest(&argc, argv); int iTestResults = RUN_ALL_TESTS(); ::GlobalTestCleanUp(); return iTestResults; } else { std::cerr << "[Error] Encountered an error while loading test parameters from provided INI file !" << std::endl; return 1; } }