mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-04-15 17:48:16 +00:00
tunnel component & update vehicle abstraction example (#8)
This commit is contained in:
@@ -85,12 +85,10 @@ add_subdirectory(unit_tests/sdv_control)
|
||||
add_subdirectory(unit_tests/sdv_macro_test)
|
||||
add_subdirectory(unit_tests/install_package_composer)
|
||||
add_subdirectory(unit_tests/path_match)
|
||||
if(UNIX)
|
||||
add_subdirectory(unit_tests/unix_sockets)
|
||||
endif()
|
||||
if(WIN32)
|
||||
add_subdirectory(unit_tests/unix_tunnel)
|
||||
add_subdirectory(unit_tests/win_sockets)
|
||||
endif()
|
||||
add_subdirectory(unit_tests/win_tunnel)
|
||||
add_subdirectory(component_tests/config)
|
||||
add_subdirectory(component_tests/logger)
|
||||
add_subdirectory(component_tests/data_dispatch_service)
|
||||
|
||||
@@ -1085,9 +1085,9 @@ ViewFilter = "Fatal"
|
||||
// Disconnect from the client
|
||||
sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(pClientConnect, nullptr);
|
||||
EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
control.RemoveConnection(tConnectionID);
|
||||
EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
ptrClientEndpoint.Clear(); // Lifetime taken over by communication control.
|
||||
|
||||
// Create another client endpoint
|
||||
@@ -1155,9 +1155,9 @@ ViewFilter = "Fatal"
|
||||
// Disconnect from the client
|
||||
sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(pClientConnect, nullptr);
|
||||
EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
control.RemoveConnection(tConnectionID);
|
||||
EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
ptrClientEndpoint.Clear(); // Lifetime taken over by communication control.
|
||||
|
||||
// Create another client endpoint
|
||||
|
||||
@@ -117,14 +117,14 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus.
|
||||
* @param[in] eConnectStatus The connection status.
|
||||
* @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
|
||||
* @param[in] eConnectState The connection state.
|
||||
*/
|
||||
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override
|
||||
virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override
|
||||
{
|
||||
switch (eConnectStatus)
|
||||
switch (eConnectState)
|
||||
{
|
||||
case sdv::ipc::EConnectStatus::disconnected:
|
||||
case sdv::ipc::EConnectState::disconnected:
|
||||
// Disconnect only when connected before
|
||||
if (m_bConnected)
|
||||
{
|
||||
@@ -133,7 +133,7 @@ public:
|
||||
m_cvDisconnect.notify_all();
|
||||
}
|
||||
break;
|
||||
case sdv::ipc::EConnectStatus::disconnected_forced:
|
||||
case sdv::ipc::EConnectState::disconnected_forced:
|
||||
// Disconnect only when connected before
|
||||
if (m_bConnected)
|
||||
{
|
||||
@@ -142,7 +142,7 @@ public:
|
||||
m_cvDisconnect.notify_all();
|
||||
}
|
||||
break;
|
||||
case sdv::ipc::EConnectStatus::connected:
|
||||
case sdv::ipc::EConnectState::connected:
|
||||
m_bConnected = true;
|
||||
break;
|
||||
default:
|
||||
@@ -339,11 +339,11 @@ extern "C" int main(int argc, char* argv[])
|
||||
if (!ptrControlConnection) return -12;
|
||||
pControlConnect = ptrControlConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
if (!pControlConnect) return -13;
|
||||
uiControlEventCookie = pControlConnect->RegisterStatusEventCallback(&receiverControl);
|
||||
uiControlEventCookie = pControlConnect->RegisterStateEventCallback(&receiverControl);
|
||||
if (!uiControlEventCookie) return -20;
|
||||
if (!pControlConnect->AsyncConnect(&receiverControl)) return -14;
|
||||
if (!pControlConnect->WaitForConnection(250)) return -5; // Note: Connection should be possible within 250ms.
|
||||
if (pControlConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -15;
|
||||
if (pControlConnect->GetConnectState() != sdv::ipc::EConnectState::connected) return -15;
|
||||
TRACE(bServer ? "Server" : "Client", ": Connected to control channel...");
|
||||
}
|
||||
|
||||
@@ -393,11 +393,11 @@ Size = 1024000
|
||||
sdv::ipc::IConnect* pDataConnect = ptrDataConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
uint64_t uiDataEventCookie = 0;
|
||||
if (!pDataConnect) return -3;
|
||||
uiDataEventCookie = pDataConnect->RegisterStatusEventCallback(&receiverData);
|
||||
uiDataEventCookie = pDataConnect->RegisterStateEventCallback(&receiverData);
|
||||
if (!uiDataEventCookie) return -21;
|
||||
if (!pDataConnect->AsyncConnect(&receiverData)) return -4;
|
||||
if (!pDataConnect->WaitForConnection(10000)) return -5; // Note: Connection should be possible within 10000ms.
|
||||
if (pDataConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -5;
|
||||
if (pDataConnect->GetConnectState() != sdv::ipc::EConnectState::connected) return -5;
|
||||
|
||||
TRACE("Connected to data channel... waiting for data exchange");
|
||||
|
||||
@@ -430,11 +430,11 @@ Size = 1024000
|
||||
}
|
||||
|
||||
// Initiate shutdown
|
||||
if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStatusEventCallback(uiDataEventCookie);
|
||||
if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStateEventCallback(uiDataEventCookie);
|
||||
ptrDataConnection.Clear();
|
||||
mgntDataMgntChannel.Shutdown();
|
||||
if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -6;
|
||||
if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStatusEventCallback(uiControlEventCookie);
|
||||
if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStateEventCallback(uiControlEventCookie);
|
||||
ptrControlConnection.Clear();
|
||||
mgntControlMgntChannel.Shutdown();
|
||||
if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -16;
|
||||
|
||||
@@ -89,33 +89,33 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus.
|
||||
* @param[in] eConnectStatus The connection status.
|
||||
* @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
|
||||
* @param[in] eConnectState The connection state.
|
||||
*/
|
||||
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override
|
||||
virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override
|
||||
{
|
||||
switch (eConnectStatus)
|
||||
switch (eConnectState)
|
||||
{
|
||||
case sdv::ipc::EConnectStatus::connection_error:
|
||||
case sdv::ipc::EConnectState::connection_error:
|
||||
m_bConnectError = true;
|
||||
break;
|
||||
case sdv::ipc::EConnectStatus::communication_error:
|
||||
case sdv::ipc::EConnectState::communication_error:
|
||||
m_bCommError = true;
|
||||
break;
|
||||
case sdv::ipc::EConnectStatus::disconnected_forced:
|
||||
case sdv::ipc::EConnectState::disconnected_forced:
|
||||
m_bForcedDisconnect = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_eStatus = eConnectStatus;
|
||||
m_eConnectState = eConnectState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the received connection status.
|
||||
* @return The received connection status.
|
||||
* @brief Return the received connection state.
|
||||
* @return The received connection state.
|
||||
*/
|
||||
sdv::ipc::EConnectStatus GetReceivedStatus() const { return m_eStatus; }
|
||||
sdv::ipc::EConnectState GetReceivedState() const { return m_eConnectState; }
|
||||
|
||||
/**
|
||||
* @brief Has a connection error occurred?
|
||||
@@ -136,9 +136,9 @@ public:
|
||||
bool ForcedDisconnectOccurred() const { return m_bForcedDisconnect; }
|
||||
|
||||
/**
|
||||
* @brief Reset the received status event flags.
|
||||
* @brief Reset the received state event flags.
|
||||
*/
|
||||
void ResetStatusEvents()
|
||||
void ResetStateEvents()
|
||||
{
|
||||
m_bConnectError = false;
|
||||
m_bCommError = false;
|
||||
@@ -182,7 +182,7 @@ private:
|
||||
sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function.
|
||||
mutable std::mutex m_mtxData; ///< Protect data access.
|
||||
sdv::sequence<sdv::pointer<uint8_t>> m_seqDataCopy; ///< Copy of the data.
|
||||
sdv::ipc::EConnectStatus m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current received status.
|
||||
sdv::ipc::EConnectState m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< Current received state.
|
||||
bool m_bConnectError = false; ///< Connection error ocurred.
|
||||
bool m_bCommError = false; ///< Communication error occurred.
|
||||
bool m_bForcedDisconnect = false; ///< Force disconnect.
|
||||
@@ -355,8 +355,8 @@ TEST(SharedMemChannelService, WaitForConnection)
|
||||
|
||||
// Wait for connection with timeout
|
||||
EXPECT_FALSE(pConnection->WaitForConnection(100));
|
||||
EXPECT_TRUE(pConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
// Wait for connection for infinite period with cancel.
|
||||
std::thread threadCancelWait([&]()
|
||||
@@ -366,8 +366,8 @@ TEST(SharedMemChannelService, WaitForConnection)
|
||||
});
|
||||
EXPECT_FALSE(pConnection->WaitForConnection(0xffffffff)); // Note: wait indefinitely. Cancel get called in 500ms.
|
||||
threadCancelWait.join();
|
||||
EXPECT_TRUE(pConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
EXPECT_NO_THROW(ptrConnection.Clear());
|
||||
|
||||
@@ -427,8 +427,8 @@ TEST(SharedMemChannelService, AsyncConnect)
|
||||
ASSERT_NE(pServerConnect, nullptr);
|
||||
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
|
||||
EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection.
|
||||
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
// Try send; should fail, since not connected
|
||||
EXPECT_FALSE(pServerSend->SendData(seqPattern));
|
||||
@@ -439,8 +439,8 @@ TEST(SharedMemChannelService, AsyncConnect)
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
EXPECT_TRUE(pServerSend->SendData(seqPattern));
|
||||
@@ -520,39 +520,39 @@ TEST(SharedMemChannelService, EstablishConnectionEvents)
|
||||
EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred());
|
||||
EXPECT_FALSE(pServerSend->SendData(seqPattern));
|
||||
EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred()); // No events registered yet... only after a call to AsyncConnect.
|
||||
receiverServer.ResetStatusEvents();
|
||||
receiverServer.ResetStateEvents();
|
||||
|
||||
// Establish the server connection
|
||||
sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(pServerConnect, nullptr);
|
||||
EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0);
|
||||
EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0);
|
||||
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
|
||||
EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection.
|
||||
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::initialized ||
|
||||
receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting);
|
||||
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
|
||||
|
||||
// Try send; should fail, since not connected
|
||||
EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred());
|
||||
EXPECT_FALSE(pServerSend->SendData(seqPattern));
|
||||
EXPECT_TRUE(receiverServer.CommunicationbErrorOccurred());
|
||||
receiverServer.ResetStatusEvents();
|
||||
receiverServer.ResetStateEvents();
|
||||
|
||||
// Establish the client connection
|
||||
sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(pClientConnection, nullptr);
|
||||
EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient), 0);
|
||||
EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient), 0);
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
|
||||
EXPECT_FALSE(receiverClient.ConnectionErrorOccurred());
|
||||
EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(receiverClient.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(receiverClient.GetReceivedState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
EXPECT_TRUE(pServerSend->SendData(seqPattern));
|
||||
@@ -621,8 +621,8 @@ TEST(SharedMemChannelService, EstablishReconnect)
|
||||
ASSERT_NE(pServerConnect, nullptr);
|
||||
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
|
||||
EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection.
|
||||
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
std::cout << "Connect client 1..." << std::endl;
|
||||
// Establish the client1 connection
|
||||
@@ -631,8 +631,8 @@ TEST(SharedMemChannelService, EstablishReconnect)
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient1));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
std::cout << "Disconnect client 1..." << std::endl;
|
||||
|
||||
@@ -640,10 +640,10 @@ TEST(SharedMemChannelService, EstablishReconnect)
|
||||
EXPECT_NO_THROW(ptrClient1Connection.Clear());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate.
|
||||
EXPECT_NO_THROW(mgntClient1.Shutdown());
|
||||
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected ||
|
||||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected ||
|
||||
pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
std::cout << "Connect client 2... (eStatus = " << (uint32_t) pServerConnect->GetStatus() << ")" << std::endl;
|
||||
std::cout << "Connect client 2... (eConnectState = " << (uint32_t) pServerConnect->GetConnectState() << ")" << std::endl;
|
||||
|
||||
// Establish the client2 connection
|
||||
sdv::TObjectPtr ptrClient2Connection = mgntClient2.Access(sChannelEndpoint.ssConnectString);
|
||||
@@ -653,8 +653,8 @@ TEST(SharedMemChannelService, EstablishReconnect)
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient2));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
std::cout << "Disconnect client 2..." << std::endl;
|
||||
|
||||
@@ -662,8 +662,8 @@ TEST(SharedMemChannelService, EstablishReconnect)
|
||||
EXPECT_NO_THROW(ptrClient2Connection.Clear());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate
|
||||
EXPECT_NO_THROW(mgntClient2.Shutdown());
|
||||
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected ||
|
||||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected ||
|
||||
pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
std::cout << "Shutdown server..." << std::endl;
|
||||
|
||||
@@ -702,13 +702,13 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
|
||||
// Establish the server connection
|
||||
sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(pServerConnect, nullptr);
|
||||
EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0);
|
||||
EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0);
|
||||
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
|
||||
EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection.
|
||||
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::initialized ||
|
||||
receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting);
|
||||
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
|
||||
|
||||
std::cout << "Connect client 1..." << std::endl;
|
||||
@@ -716,16 +716,16 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
|
||||
// Establish the client1 connection
|
||||
sdv::ipc::IConnect* pClientConnection = ptrClient1Connection.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(pClientConnection, nullptr);
|
||||
EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient1), 0);
|
||||
EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient1), 0);
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient1));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
|
||||
EXPECT_FALSE(receiverClient1.ConnectionErrorOccurred());
|
||||
EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(receiverClient1.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(receiverClient1.GetReceivedState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
std::cout << "Disconnect client 1..." << std::endl;
|
||||
|
||||
@@ -733,10 +733,10 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
|
||||
EXPECT_NO_THROW(ptrClient1Connection.Clear());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate.
|
||||
EXPECT_NO_THROW(mgntClient1.Shutdown());
|
||||
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected ||
|
||||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::disconnected ||
|
||||
receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected ||
|
||||
pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::disconnected ||
|
||||
receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting);
|
||||
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
|
||||
|
||||
std::cout << "Connect client 2..." << std::endl;
|
||||
@@ -746,16 +746,16 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
|
||||
EXPECT_TRUE(ptrClient2Connection);
|
||||
pClientConnection = ptrClient2Connection.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(pClientConnection, nullptr);
|
||||
EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient2), 0);
|
||||
EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient2), 0);
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient2));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
|
||||
EXPECT_FALSE(receiverClient2.ConnectionErrorOccurred());
|
||||
EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(receiverClient2.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(receiverClient2.GetReceivedState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
std::cout << "Disconnect client 2..." << std::endl;
|
||||
|
||||
@@ -763,10 +763,10 @@ TEST(SharedMemChannelService, EstablishReconnectEvents)
|
||||
EXPECT_NO_THROW(ptrClient2Connection.Clear());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Note: Handle connection terminate
|
||||
EXPECT_NO_THROW(mgntClient2.Shutdown());
|
||||
EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected ||
|
||||
pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::disconnected ||
|
||||
receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected ||
|
||||
pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::disconnected ||
|
||||
receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting);
|
||||
EXPECT_FALSE(receiverServer.ConnectionErrorOccurred());
|
||||
|
||||
std::cout << "Shutdown server..." << std::endl;
|
||||
@@ -828,7 +828,7 @@ Mode = "Essential")code"));
|
||||
ASSERT_NE(pServerConnect, nullptr);
|
||||
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
EXPECT_TRUE(pServerSend->SendData(seqPattern));
|
||||
@@ -909,11 +909,11 @@ Mode = "Essential")code"));
|
||||
ASSERT_NE(pServerConnect, nullptr);
|
||||
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
auto tpStart = std::chrono::high_resolution_clock::now();
|
||||
while (pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connected)
|
||||
while (pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connected)
|
||||
{
|
||||
EXPECT_TRUE(pServerSend->SendData(seqPattern));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
@@ -925,7 +925,7 @@ Mode = "Essential")code"));
|
||||
break; // Max 4000 ms
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
EXPECT_NO_THROW(ptrServerConnection.Clear());
|
||||
|
||||
@@ -984,14 +984,14 @@ Mode = "Essential")code"));
|
||||
// Establish the server connection
|
||||
sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(pServerConnect, nullptr);
|
||||
EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0);
|
||||
EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0);
|
||||
EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer));
|
||||
EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
auto tpStart = std::chrono::high_resolution_clock::now();
|
||||
while (pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connected)
|
||||
while (pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connected)
|
||||
{
|
||||
EXPECT_TRUE(pServerSend->SendData(seqPattern));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
@@ -1004,7 +1004,7 @@ Mode = "Essential")code"));
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(receiverServer.ForcedDisconnectOccurred());
|
||||
EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
EXPECT_NO_THROW(ptrServerConnection.Clear());
|
||||
|
||||
@@ -1048,7 +1048,7 @@ Mode = "Essential")code"));
|
||||
ASSERT_NE(pControlConnect1, nullptr);
|
||||
EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1));
|
||||
EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Wait for data
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString;
|
||||
@@ -1084,7 +1084,7 @@ Mode = "Essential")code"));
|
||||
ASSERT_NE(pControlConnect2, nullptr);
|
||||
EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2));
|
||||
EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Wait for process termination
|
||||
pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff);
|
||||
@@ -1131,7 +1131,7 @@ Mode = "Essential")code"));
|
||||
ASSERT_NE(pControlConnect1, nullptr);
|
||||
EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1));
|
||||
EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 5000ms.
|
||||
EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Wait for connection string (max. 500*10ms).
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString;
|
||||
@@ -1167,7 +1167,7 @@ Mode = "Essential")code"));
|
||||
ASSERT_NE(pControlConnect2, nullptr);
|
||||
EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2));
|
||||
EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Wait for process termination
|
||||
pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff);
|
||||
@@ -1212,7 +1212,7 @@ Mode = "Essential")code"));
|
||||
ASSERT_NE(pControlConnect1, nullptr);
|
||||
EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1));
|
||||
EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Wait for connection string (max. 500*10ms).
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqDataConnectString;
|
||||
@@ -1248,7 +1248,7 @@ Mode = "Essential")code"));
|
||||
ASSERT_NE(pControlConnect2, nullptr);
|
||||
EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2));
|
||||
EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Wait for process termination
|
||||
pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff);
|
||||
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
// Wait until there is no activity any more.
|
||||
std::chrono::high_resolution_clock::time_point tpTickSent = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::high_resolution_clock::time_point tpTickReceive = std::chrono::high_resolution_clock::now();
|
||||
while (m_eStatus != sdv::ipc::EConnectStatus::disconnected)
|
||||
while (m_eConnectState != sdv::ipc::EConnectState::disconnected)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxData);
|
||||
m_cvReceived.wait_for(lock, std::chrono::milliseconds(50));
|
||||
@@ -141,33 +141,33 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus.
|
||||
* @param[in] eConnectStatus The connection status.
|
||||
* @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
|
||||
* @param[in] eConnectState The connection state.
|
||||
*/
|
||||
virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override
|
||||
virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override
|
||||
{
|
||||
switch (eConnectStatus)
|
||||
switch (eConnectState)
|
||||
{
|
||||
case sdv::ipc::EConnectStatus::connection_error:
|
||||
case sdv::ipc::EConnectState::connection_error:
|
||||
m_bConnectError = true;
|
||||
break;
|
||||
case sdv::ipc::EConnectStatus::communication_error:
|
||||
case sdv::ipc::EConnectState::communication_error:
|
||||
m_bCommError = true;
|
||||
break;
|
||||
case sdv::ipc::EConnectStatus::disconnected_forced:
|
||||
case sdv::ipc::EConnectState::disconnected_forced:
|
||||
m_bForcedDisconnect = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_eStatus = eConnectStatus;
|
||||
m_eConnectState = eConnectState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the received connection status.
|
||||
* @return The received connection status.
|
||||
* @brief Return the received connection state.
|
||||
* @return The received connection state.
|
||||
*/
|
||||
sdv::ipc::EConnectStatus GetReceivedStatus() const { return m_eStatus; }
|
||||
sdv::ipc::EConnectState GetReceivedState() const { return m_eConnectState; }
|
||||
|
||||
/**
|
||||
* @brief Has a connection error occurred?
|
||||
@@ -207,9 +207,9 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the received status event flags.
|
||||
* @brief Reset the received state event flags.
|
||||
*/
|
||||
void ResetStatusEvents()
|
||||
void ResetStateEvents()
|
||||
{
|
||||
m_bConnectError = false;
|
||||
m_bCommError = false;
|
||||
@@ -256,7 +256,7 @@ private:
|
||||
sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function.
|
||||
mutable std::mutex m_mtxData; ///< Protect data access.
|
||||
std::queue<sdv::pointer<uint8_t>> m_queueDataCopy; ///< Copy of the data.
|
||||
std::atomic<sdv::ipc::EConnectStatus> m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current received status.
|
||||
std::atomic<sdv::ipc::EConnectState> m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< Current received state.
|
||||
bool m_bConnectError = false; ///< Connection error ocurred.
|
||||
bool m_bCommError = false; ///< Communication error occurred.
|
||||
bool m_bForcedDisconnect = false; ///< Force disconnect.
|
||||
@@ -322,8 +322,8 @@ Size = 1024000
|
||||
ASSERT_NE(pServerConnection, nullptr);
|
||||
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
|
||||
EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection.
|
||||
EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
// Establish the client connection
|
||||
sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
@@ -331,8 +331,8 @@ Size = 1024000
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
appcontrol.SetRunningMode();
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
@@ -419,8 +419,8 @@ Size = 1024000
|
||||
ASSERT_NE(pServerConnection, nullptr);
|
||||
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
|
||||
EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection.
|
||||
EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
// Establish the client connection
|
||||
sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
@@ -428,8 +428,8 @@ Size = 1024000
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
appcontrol.SetRunningMode();
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
@@ -551,8 +551,8 @@ Size = 1024000
|
||||
ASSERT_NE(pServerConnection, nullptr);
|
||||
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
|
||||
EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection.
|
||||
EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized ||
|
||||
pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting);
|
||||
EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized ||
|
||||
pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting);
|
||||
|
||||
// Establish the client connection
|
||||
sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
@@ -560,8 +560,8 @@ Size = 1024000
|
||||
EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient));
|
||||
EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server.
|
||||
EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
appcontrol.SetRunningMode();
|
||||
|
||||
// Try send; should succeed, since connected
|
||||
@@ -666,7 +666,7 @@ Size = 1024000
|
||||
ASSERT_NE(pServerConnection, nullptr);
|
||||
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
|
||||
EXPECT_TRUE(pServerConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
TRACE("Connection estabished...");
|
||||
appcontrol.SetRunningMode();
|
||||
|
||||
@@ -750,7 +750,7 @@ Size = 1024000
|
||||
ASSERT_NE(pServerConnection, nullptr);
|
||||
EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer));
|
||||
EXPECT_TRUE(pServerConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms.
|
||||
EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
TRACE("Connection estabished...");
|
||||
appcontrol.SetRunningMode();
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Contributors:
|
||||
# Denisa Ros - initial API and implementation
|
||||
#*******************************************************************************
|
||||
|
||||
if(UNIX)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(UnixSocketCommunicationTests LANGUAGES CXX)
|
||||
@@ -26,11 +29,19 @@ target_include_directories(UnitTest_UnixSocketConnectTests PUBLIC
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_link_libraries(UnitTest_UnixSocketConnectTests
|
||||
GTest::GTest ${CMAKE_THREAD_LIBS_INIT} stdc++fs ${CMAKE_DL_LIBS} rt
|
||||
GTest::GTest
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
stdc++fs ${CMAKE_DL_LIBS}
|
||||
rt
|
||||
uds_unix_sockets
|
||||
)
|
||||
else()
|
||||
target_link_libraries(UnitTest_UnixSocketConnectTests
|
||||
GTest::GTest Ws2_32 Winmm Rpcrt4.lib
|
||||
GTest::GTest
|
||||
Ws2_32
|
||||
Winmm
|
||||
Rpcrt4.lib
|
||||
uds_unix_sockets
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -46,4 +57,6 @@ add_custom_command(TARGET UnitTest_UnixSocketConnectTests POST_BUILD
|
||||
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixSocketConnectTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
@@ -1,12 +1,15 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
********************************************************************************/
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined __unix__
|
||||
|
||||
@@ -15,8 +18,8 @@
|
||||
#include <support/app_control.h>
|
||||
#include <interfaces/ipc.h>
|
||||
|
||||
#include "../../../sdv_services/uds_unix_sockets/channel_mgnt.cpp"
|
||||
#include "../../../sdv_services/uds_unix_sockets/connection.cpp"
|
||||
#include "../../../sdv_services/uds_unix_sockets/channel_mgnt.h"
|
||||
#include "../../../sdv_services/uds_unix_sockets/connection.h"
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
@@ -24,6 +27,7 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
@@ -44,24 +48,24 @@ public:
|
||||
// don't test data path yet
|
||||
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& /*seqData*/) override {}
|
||||
|
||||
void SetStatus(sdv::ipc::EConnectStatus s) override {
|
||||
void SetConnectState(sdv::ipc::EConnectState s) override {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_status = s;
|
||||
m_state = s;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000)
|
||||
bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
|
||||
if (m_status == expected)
|
||||
if (m_state == expected)
|
||||
return true;
|
||||
|
||||
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
|
||||
|
||||
while (m_status != expected)
|
||||
while (m_state != expected)
|
||||
{
|
||||
if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout)
|
||||
return false;
|
||||
@@ -69,13 +73,13 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::ipc::EConnectStatus GetStatus() const {
|
||||
sdv::ipc::EConnectState GetConnectState() const {
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
return m_status;
|
||||
return m_state;
|
||||
}
|
||||
|
||||
private:
|
||||
sdv::ipc::EConnectStatus m_status { sdv::ipc::EConnectStatus::uninitialized };
|
||||
sdv::ipc::EConnectState m_state { sdv::ipc::EConnectState::uninitialized };
|
||||
mutable std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
};
|
||||
@@ -101,24 +105,24 @@ public:
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
void SetStatus(sdv::ipc::EConnectStatus s) override {
|
||||
void SetConnectState(sdv::ipc::EConnectState s) override {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_status = s;
|
||||
m_state = s;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000)
|
||||
bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
|
||||
if (m_status == expected)
|
||||
if (m_state == expected)
|
||||
return true;
|
||||
|
||||
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
|
||||
|
||||
while (m_status != expected)
|
||||
while (m_state != expected)
|
||||
{
|
||||
if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout)
|
||||
return false;
|
||||
@@ -141,13 +145,13 @@ public:
|
||||
private:
|
||||
mutable std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
sdv::ipc::EConnectStatus m_status{ sdv::ipc::EConnectStatus::uninitialized };
|
||||
sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized };
|
||||
sdv::sequence<sdv::pointer<uint8_t>> m_lastData;
|
||||
bool m_received{ false };
|
||||
};
|
||||
|
||||
|
||||
// A receiver that intentionally throws from SetStatus(...) to test callback-safety.
|
||||
// A receiver that intentionally throws from SetConnectState(...) to test callback-safety.
|
||||
class CUDSThrowingReceiver :
|
||||
public sdv::IInterfaceAccess,
|
||||
public sdv::ipc::IDataReceiveCallback,
|
||||
@@ -161,9 +165,9 @@ public:
|
||||
|
||||
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& /*seq*/) override {}
|
||||
|
||||
void SetStatus(sdv::ipc::EConnectStatus s) override
|
||||
void SetConnectState(sdv::ipc::EConnectState s) override
|
||||
{
|
||||
// Store the last status and then throw to simulate misbehaving user code.
|
||||
// Store the last state and then throw to simulate misbehaving user code.
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_last = s;
|
||||
@@ -172,7 +176,7 @@ public:
|
||||
throw std::runtime_error("Intentional user callback failure");
|
||||
}
|
||||
|
||||
bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000)
|
||||
bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
|
||||
@@ -193,7 +197,7 @@ public:
|
||||
private:
|
||||
std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
sdv::ipc::EConnectStatus m_last{ sdv::ipc::EConnectStatus::uninitialized };
|
||||
sdv::ipc::EConnectState m_last{ sdv::ipc::EConnectState::uninitialized };
|
||||
};
|
||||
|
||||
|
||||
@@ -328,7 +332,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect)
|
||||
|
||||
if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; }
|
||||
|
||||
if (clientConn->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult = 5; return; }
|
||||
if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; }
|
||||
|
||||
// Wait for the server to be conected before disconecting the client
|
||||
while (!allowClientDisconnect.load())
|
||||
@@ -339,7 +343,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect)
|
||||
|
||||
// SERVER must also report connected
|
||||
EXPECT_TRUE(serverConn->WaitForConnection(5000));
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Allow client to dissconect now, because the Server is connected
|
||||
allowClientDisconnect = true;
|
||||
@@ -348,7 +352,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect)
|
||||
|
||||
//DISCONNECT both
|
||||
serverConn->Disconnect();
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
// Shutdown Manager / Framework
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
@@ -403,7 +407,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
|
||||
if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; }
|
||||
|
||||
if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; }
|
||||
if (clientConn->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult = 5; return; }
|
||||
if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; }
|
||||
|
||||
//waits for confirmation before disconect
|
||||
while (!allowClientDisconnect.load())
|
||||
@@ -415,7 +419,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
|
||||
|
||||
// Server has to be connected (same timeout with client?)
|
||||
EXPECT_TRUE(serverConn->WaitForConnection(5000)) << "Server didn't reach 'connected' in time";
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Allows Client to disconnect and waits for finishing
|
||||
allowClientDisconnect = true;
|
||||
@@ -424,7 +428,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
|
||||
|
||||
// Disconnect server
|
||||
serverConn->Disconnect();
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
// SESSION 2
|
||||
// SERVER
|
||||
@@ -450,7 +454,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
|
||||
if (!clientConn2->AsyncConnect(&cRcvr2)) { clientResult2 = 3; return; }
|
||||
|
||||
if (!clientConn2->WaitForConnection(5000)) { clientResult2 = 4; return; }
|
||||
if (clientConn2->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult2 = 5; return; }
|
||||
if (clientConn2->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult2 = 5; return; }
|
||||
|
||||
//waits for confirmation before disconect
|
||||
while (!allowClientDisconnect2.load())
|
||||
@@ -463,7 +467,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
|
||||
// Server has to be connected
|
||||
// if unlink(path) from session 1 worked, bind/listen/accept works again
|
||||
EXPECT_TRUE(serverConn2->WaitForConnection(5000)) << "Server didn't reach 'connected' in time (session 2)";
|
||||
EXPECT_EQ(serverConn2->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(serverConn2->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Allows Client to disconnect and waits for finishing
|
||||
allowClientDisconnect2 = true;
|
||||
@@ -472,7 +476,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath)
|
||||
|
||||
// Disconnect server
|
||||
serverConn2->Disconnect();
|
||||
EXPECT_EQ(serverConn2->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(serverConn2->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
//Shutdown manager/framework
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
@@ -550,8 +554,8 @@ TEST(UnixSocketIPC, CreateEndpoint_WithConfigAndPathClamping)
|
||||
|
||||
EXPECT_TRUE(serverConn->WaitForConnection(5000));
|
||||
EXPECT_TRUE(clientConn->WaitForConnection(5000));
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Cleanup
|
||||
clientConn->Disconnect();
|
||||
@@ -592,8 +596,8 @@ TEST(UnixSocketIPC, Access_DefaultPath_ServerClientConnect)
|
||||
|
||||
EXPECT_TRUE(serverConn->WaitForConnection(5000));
|
||||
EXPECT_TRUE(clientConn->WaitForConnection(5000));
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
clientConn->Disconnect();
|
||||
serverConn->Disconnect();
|
||||
@@ -637,7 +641,7 @@ TEST(UnixSocketIPC, WaitForConnection_InfiniteWait_SlowClient)
|
||||
|
||||
// INFINITE wait (0xFFFFFFFFu)
|
||||
EXPECT_TRUE(serverConn->WaitForConnection(0xFFFFFFFFu));
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Cleanup
|
||||
delayedClient.join();
|
||||
@@ -691,7 +695,8 @@ TEST(UnixSocketIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter)
|
||||
TEST(UnixSocketIPC, ClientTimeout_NoServer)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
app.SetRunningMode();
|
||||
|
||||
CUnixDomainSocketsChannelMgnt mgr;
|
||||
@@ -714,7 +719,7 @@ TEST(UnixSocketIPC, ClientTimeout_NoServer)
|
||||
EXPECT_FALSE(clientConn->WaitForConnection(1500));
|
||||
// after another ~1s it should be in connection_error
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(800));
|
||||
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connection_error);
|
||||
EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connection_error);
|
||||
|
||||
clientConn->Disconnect(); // cleanup (joins threads)
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
@@ -754,9 +759,9 @@ TEST(UnixSocketIPC, ServerDisconnectPropagatesToClient)
|
||||
serverConn->Disconnect();
|
||||
|
||||
// Deterministic wait for client-side transition to 'disconnected'
|
||||
EXPECT_TRUE(cRcvr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, /*ms*/ 3000)) << "Client did not observe 'disconnected' after server closed the socket.";
|
||||
EXPECT_TRUE(cRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, /*ms*/ 3000)) << "Client did not observe 'disconnected' after server closed the socket.";
|
||||
|
||||
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
clientConn->Disconnect();
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
@@ -794,7 +799,7 @@ TEST(UnixSocketIPC, ReconnectOnSameServerInstance)
|
||||
ASSERT_TRUE(clientConn->WaitForConnection(5000));
|
||||
clientConn->Disconnect();
|
||||
serverConn->Disconnect();
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
}
|
||||
|
||||
// Second session on the same serverConn object
|
||||
@@ -1129,7 +1134,7 @@ TEST(UnixSocketIPC, PeerCloseMidTransfer_ClientSeesDisconnected_AndSendMayFailOr
|
||||
std::cout << "[Debug] SendData result = " << sendResult.load() << std::endl;
|
||||
|
||||
// But client MUST observe disconnected:
|
||||
EXPECT_TRUE(cRcvr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000));
|
||||
EXPECT_TRUE(cRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000));
|
||||
|
||||
clientConn->Disconnect();
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
@@ -1162,8 +1167,8 @@ TEST(UnixSocketIPC, ClientCancelConnect_NoServer_CleansUpPromptly)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
||||
clientConn->Disconnect();
|
||||
|
||||
// Immediately after disconnect, status should be 'disconnected' (no hangs).
|
||||
EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
// Immediately after disconnect, state should be 'disconnected' (no hangs).
|
||||
EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
EXPECT_NO_THROW(mgr.Shutdown()); app.Shutdown();
|
||||
}
|
||||
@@ -1172,7 +1177,8 @@ TEST(UnixSocketIPC, ClientCancelConnect_NoServer_CleansUpPromptly)
|
||||
TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
app.SetRunningMode();
|
||||
CUnixDomainSocketsChannelMgnt mgr;
|
||||
EXPECT_NO_THROW(mgr.Initialize(""));
|
||||
@@ -1192,15 +1198,15 @@ TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient)
|
||||
// The server may be still listening; ensure we can disconnect cleanly
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
serverConn->Disconnect();
|
||||
EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
|
||||
//Callback throws in SetStatus -> transport threads keep running
|
||||
TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport)
|
||||
//Callback throws in SetConnectState -> transport threads keep running
|
||||
TEST(UnixSocketIPC, CallbackThrowsInSetConnectState_DoesNotCrashTransport)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
@@ -1227,7 +1233,7 @@ TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport)
|
||||
CUDSThrowingReceiver cRcvr;
|
||||
ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr));
|
||||
|
||||
// Despite exceptions thrown inside SetStatus, the transport should still reach connected.
|
||||
// Despite exceptions thrown inside SetConnectState, the transport should still reach connected.
|
||||
EXPECT_TRUE(serverConn->WaitForConnection(5000));
|
||||
EXPECT_TRUE(clientConn->WaitForConnection(5000));
|
||||
|
||||
@@ -1239,8 +1245,8 @@ TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport)
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
//RegisterStatusEventCallback: multiple listeners receive status updates
|
||||
TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus)
|
||||
//RegisterStateEventCallback: multiple listeners receive state updates
|
||||
TEST(UnixSocketIPC, RegisterStateEventCallback_MultipleCallbacksReceiveState)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
@@ -1266,12 +1272,12 @@ TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus)
|
||||
|
||||
// --- Callback receiver 1 ---
|
||||
CUDSConnectReceiver recv1;
|
||||
uint64_t cookie1 = server->RegisterStatusEventCallback(&recv1);
|
||||
uint64_t cookie1 = server->RegisterStateEventCallback(&recv1);
|
||||
EXPECT_NE(cookie1, 0u);
|
||||
|
||||
// --- Callback receiver 2 ---
|
||||
CUDSConnectReceiver recv2;
|
||||
uint64_t cookie2 = server->RegisterStatusEventCallback(&recv2);
|
||||
uint64_t cookie2 = server->RegisterStateEventCallback(&recv2);
|
||||
EXPECT_NE(cookie2, 0u);
|
||||
EXPECT_NE(cookie1, cookie2);
|
||||
|
||||
@@ -1283,22 +1289,22 @@ TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus)
|
||||
ASSERT_TRUE(client->WaitForConnection(5000));
|
||||
|
||||
// Both receivers should have received 'connected'
|
||||
EXPECT_TRUE(recv1.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000));
|
||||
EXPECT_TRUE(recv2.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000));
|
||||
EXPECT_TRUE(recv1.WaitForState(sdv::ipc::EConnectState::connected, 1000));
|
||||
EXPECT_TRUE(recv2.WaitForState(sdv::ipc::EConnectState::connected, 1000));
|
||||
|
||||
// --- Disconnect ---
|
||||
client->Disconnect();
|
||||
|
||||
EXPECT_TRUE(recv1.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000));
|
||||
EXPECT_TRUE(recv2.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000));
|
||||
EXPECT_TRUE(recv1.WaitForState(sdv::ipc::EConnectState::disconnected, 1000));
|
||||
EXPECT_TRUE(recv2.WaitForState(sdv::ipc::EConnectState::disconnected, 1000));
|
||||
|
||||
server->Disconnect();
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
//UnregisterStatusEventCallback: removed listener stops receiving events
|
||||
TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving)
|
||||
//UnregisterStateEventCallback: removed listener stops receiving events
|
||||
TEST(UnixSocketIPC, UnregisterStateEventCallback_RemovedListenerStopsReceiving)
|
||||
{
|
||||
// Framework + Manager
|
||||
sdv::app::CAppControl app;
|
||||
@@ -1328,13 +1334,13 @@ TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving)
|
||||
ASSERT_NE(client, nullptr);
|
||||
|
||||
// --------- Two distinct receivers ----------
|
||||
// recvReg: used ONLY for the status-callback registry
|
||||
// recvReg: used ONLY for the state-callback registry
|
||||
// recvConn: used ONLY for AsyncConnect (m_pReceiver / m_pEvent path)
|
||||
CUDSConnectReceiver recvReg;
|
||||
CUDSConnectReceiver recvConn;
|
||||
|
||||
// Register recvReg as a status listener (registry path)
|
||||
uint64_t cookie = server->RegisterStatusEventCallback(&recvReg);
|
||||
// Register recvReg as a state listener (registry path)
|
||||
uint64_t cookie = server->RegisterStateEventCallback(&recvReg);
|
||||
ASSERT_NE(cookie, 0u) << "Cookie must be non-zero";
|
||||
|
||||
// Start connections (server uses recvReg only for registry; recvConn is the IConnect/IDataReceive side)
|
||||
@@ -1346,21 +1352,21 @@ TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving)
|
||||
ASSERT_TRUE(client->WaitForConnection(5000));
|
||||
|
||||
// Both the connection receiver (recvConn) and the registry listener (recvReg) should observe 'connected'
|
||||
EXPECT_TRUE(recvConn.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)) << "Connection receiver didn't see 'connected'.";
|
||||
EXPECT_TRUE(recvReg.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)) << "Registry listener didn't see 'connected'.";
|
||||
EXPECT_TRUE(recvConn.WaitForState(sdv::ipc::EConnectState::connected, 1000)) << "Connection receiver didn't see 'connected'.";
|
||||
EXPECT_TRUE(recvReg.WaitForState(sdv::ipc::EConnectState::connected, 1000)) << "Registry listener didn't see 'connected'.";
|
||||
|
||||
// --------- Unregister the registry listener ----------
|
||||
server->UnregisterStatusEventCallback(cookie);
|
||||
server->UnregisterStateEventCallback(cookie);
|
||||
|
||||
// Trigger a disconnect on client to force status transitions
|
||||
// Trigger a disconnect on client to force state transitions
|
||||
client->Disconnect();
|
||||
|
||||
// The connection receiver (recvConn) still sees disconnected (normal path)
|
||||
EXPECT_TRUE(recvConn.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)) << "Connection receiver didn't see 'disconnected'.";
|
||||
EXPECT_TRUE(recvConn.WaitForState(sdv::ipc::EConnectState::disconnected, 1000)) << "Connection receiver didn't see 'disconnected'.";
|
||||
|
||||
// The registry listener (recvReg) MUST NOT receive 'disconnected' anymore
|
||||
// (wait a short, deterministic interval)
|
||||
EXPECT_FALSE(recvReg.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 300)) << "Registry listener received 'disconnected' after UnregisterStatusEventCallback().";
|
||||
EXPECT_FALSE(recvReg.WaitForState(sdv::ipc::EConnectState::disconnected, 300)) << "Registry listener received 'disconnected' after UnregisterStateEventCallback().";
|
||||
|
||||
// Server cleanup
|
||||
server->Disconnect();
|
||||
|
||||
107
tests/unit_tests/unix_tunnel/CMakeLists.txt
Normal file
107
tests/unit_tests/unix_tunnel/CMakeLists.txt
Normal file
@@ -0,0 +1,107 @@
|
||||
#*******************************************************************************
|
||||
# Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Apache License Version 2.0 which is available at
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Contributors:
|
||||
# Denisa Ros - initial API and implementation
|
||||
#*******************************************************************************
|
||||
if(UNIX)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(UnixTunnelCommunicationTests LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Executable: GTest for UDS connect
|
||||
add_executable(UnitTest_UnixTunnelConnectTests
|
||||
unix_tunnel_connect_tests.cpp
|
||||
)
|
||||
|
||||
target_include_directories(UnitTest_UnixTunnelConnectTests PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_link_libraries(UnitTest_UnixTunnelConnectTests
|
||||
GTest::GTest
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
stdc++fs
|
||||
${CMAKE_DL_LIBS}
|
||||
rt
|
||||
uds_unix_tunnel
|
||||
uds_unix_sockets
|
||||
)
|
||||
else()
|
||||
target_link_libraries(UnitTest_UnixTunnelConnectTests
|
||||
GTest::GTest
|
||||
Ws2_32 Winmm
|
||||
Rpcrt4.lib
|
||||
uds_unix_tunnel
|
||||
uds_unix_sockets
|
||||
)
|
||||
endif()
|
||||
|
||||
add_test(NAME UnitTest_UnixTunnelConnectTests
|
||||
COMMAND UnitTest_UnixTunnelConnectTests)
|
||||
|
||||
add_dependencies(UnitTest_UnixTunnelConnectTests dependency_sdv_components)
|
||||
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
add_custom_command(TARGET UnitTest_UnixTunnelConnectTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
|
||||
"$<TARGET_FILE:UnitTest_UnixTunnelConnectTests>"
|
||||
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixTunnelConnectTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(UnitTest_UnixTunnelChannelMgntTests
|
||||
unix_tunnel_channel_mgnt_tests.cpp
|
||||
)
|
||||
|
||||
target_include_directories(UnitTest_UnixTunnelChannelMgntTests PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_link_libraries(UnitTest_UnixTunnelChannelMgntTests
|
||||
GTest::GTest
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
stdc++fs
|
||||
${CMAKE_DL_LIBS}
|
||||
rt
|
||||
uds_unix_tunnel
|
||||
uds_unix_sockets
|
||||
)
|
||||
else()
|
||||
target_link_libraries(UnitTest_UnixTunnelChannelMgntTests
|
||||
GTest::GTest
|
||||
Ws2_32
|
||||
Winmm
|
||||
Rpcrt4.lib
|
||||
uds_unix_tunnel
|
||||
uds_unix_sockets
|
||||
)
|
||||
endif()
|
||||
|
||||
add_test(NAME UnitTest_UnixTunnelChannelMgntTests
|
||||
COMMAND UnitTest_UnixTunnelChannelMgntTests)
|
||||
|
||||
add_dependencies(UnitTest_UnixTunnelChannelMgntTests dependency_sdv_components)
|
||||
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
add_custom_command(TARGET UnitTest_UnixTunnelChannelMgntTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
|
||||
"$<TARGET_FILE:UnitTest_UnixTunnelChannelMgntTests>"
|
||||
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixTunnelChannelMgntTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
240
tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp
Normal file
240
tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#if defined(__unix__)
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <support/app_control.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include <support/interface_ptr.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "../sdv_services/uds_unix_tunnel/channel_mgnt.h"
|
||||
#include "../sdv_services/uds_unix_tunnel/connection.h"
|
||||
#include "../sdv_services/uds_unix_sockets/connection.h"
|
||||
|
||||
// Reuse the same CTunnelTestReceiver from unix_tunnel_connect_tests
|
||||
class CTunnelMgrTestReceiver :
|
||||
public sdv::IInterfaceAccess,
|
||||
public sdv::ipc::IDataReceiveCallback,
|
||||
public sdv::ipc::IConnectEventCallback
|
||||
{
|
||||
public:
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& seqData) override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_lastData = seqData;
|
||||
m_received = true;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
void SetConnectState(sdv::ipc::EConnectState s) override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_state = s;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
if (m_state == expected)
|
||||
return true;
|
||||
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
|
||||
while (m_state != expected)
|
||||
{
|
||||
if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaitForData(uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
return m_cv.wait_for(lk, std::chrono::milliseconds(ms), [&]{ return m_received; });
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> GetLastData() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
return m_lastData;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized };
|
||||
sdv::sequence<sdv::pointer<uint8_t>> m_lastData;
|
||||
bool m_received{ false };
|
||||
};
|
||||
|
||||
//Manager instantiate + lifecycle
|
||||
TEST(UnixTunnelChannelMgnt, InstantiateAndLifecycle)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
CUnixTunnelChannelMgnt mgr;
|
||||
|
||||
EXPECT_NO_THROW(mgr.Initialize(""));
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized);
|
||||
|
||||
EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::configuring));
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::configuring);
|
||||
|
||||
EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running));
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::running);
|
||||
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending);
|
||||
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// CreateEndpoint -> Access(server/client) -> AsyncConnect -> Wait -> Disconnect
|
||||
TEST(UnixTunnelChannelMgnt, BasicConnectDisconnect)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CUnixTunnelChannelMgnt mgr;
|
||||
EXPECT_NO_THROW(mgr.Initialize(""));
|
||||
EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running));
|
||||
ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running);
|
||||
|
||||
// Create a tunnel endpoint (server)
|
||||
auto ep = mgr.CreateEndpoint("");
|
||||
ASSERT_NE(ep.pConnection, nullptr);
|
||||
ASSERT_FALSE(ep.ssConnectString.empty());
|
||||
|
||||
const std::string serverCS = ep.ssConnectString;
|
||||
// Convert to client by role=client
|
||||
std::string clientCS = serverCS;
|
||||
{
|
||||
const std::string from = "role=server";
|
||||
const std::string to = "role=client";
|
||||
auto pos = clientCS.find(from);
|
||||
if (pos != std::string::npos)
|
||||
clientCS.replace(pos, from.size(), to);
|
||||
}
|
||||
|
||||
// SERVER
|
||||
sdv::TObjectPtr serverObj = mgr.Access(serverCS);
|
||||
ASSERT_TRUE(serverObj);
|
||||
auto* serverConn = serverObj.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(serverConn, nullptr);
|
||||
|
||||
CTunnelMgrTestReceiver sRcvr;
|
||||
ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr));
|
||||
|
||||
// CLIENT (thread)
|
||||
std::atomic<int> clientResult{0};
|
||||
std::thread clientThread([&]{
|
||||
sdv::TObjectPtr clientObj = mgr.Access(clientCS);
|
||||
if (!clientObj) { clientResult = 1; return; }
|
||||
auto* clientConn = clientObj.GetInterface<sdv::ipc::IConnect>();
|
||||
if (!clientConn) { clientResult = 2; return; }
|
||||
CTunnelMgrTestReceiver cRcvr;
|
||||
if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; }
|
||||
if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; }
|
||||
if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; }
|
||||
clientConn->Disconnect();
|
||||
clientResult = 0;
|
||||
});
|
||||
|
||||
EXPECT_TRUE(serverConn->WaitForConnection(5000));
|
||||
EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
clientThread.join();
|
||||
EXPECT_EQ(clientResult.load(), 0);
|
||||
|
||||
serverConn->Disconnect();
|
||||
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Data path: "hello" via channel manager (using proto=tunnel)
|
||||
TEST(UnixTunnelChannelMgnt, DataPath_SimpleHello_ViaManager)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CUnixTunnelChannelMgnt mgr;
|
||||
EXPECT_NO_THROW(mgr.Initialize(""));
|
||||
EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running));
|
||||
ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running);
|
||||
|
||||
auto ep = mgr.CreateEndpoint("");
|
||||
ASSERT_FALSE(ep.ssConnectString.empty());
|
||||
const std::string serverCS = ep.ssConnectString;
|
||||
|
||||
std::string clientCS = serverCS;
|
||||
{
|
||||
const std::string from = "role=server";
|
||||
const std::string to = "role=client";
|
||||
auto pos = clientCS.find(from);
|
||||
if (pos != std::string::npos)
|
||||
clientCS.replace(pos, from.size(), to);
|
||||
}
|
||||
|
||||
// Server
|
||||
sdv::TObjectPtr serverObj = mgr.Access(serverCS);
|
||||
ASSERT_TRUE(serverObj);
|
||||
auto* serverConn = serverObj.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(serverConn, nullptr);
|
||||
CTunnelMgrTestReceiver sRcvr;
|
||||
ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr));
|
||||
|
||||
// Client
|
||||
sdv::TObjectPtr clientObj = mgr.Access(clientCS);
|
||||
ASSERT_TRUE(clientObj);
|
||||
auto* clientConn = clientObj.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(clientConn, nullptr);
|
||||
CTunnelMgrTestReceiver cRcvr;
|
||||
ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr));
|
||||
|
||||
EXPECT_TRUE(serverConn->WaitForConnection(5000));
|
||||
EXPECT_TRUE(clientConn->WaitForConnection(5000));
|
||||
|
||||
// Payload "hello"
|
||||
sdv::pointer<uint8_t> p;
|
||||
p.resize(5);
|
||||
std::memcpy(p.get(), "hello", 5);
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(p);
|
||||
|
||||
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientConn);
|
||||
ASSERT_NE(pSend, nullptr);
|
||||
EXPECT_TRUE(pSend->SendData(seq));
|
||||
|
||||
EXPECT_TRUE(sRcvr.WaitForData(3000));
|
||||
sdv::sequence<sdv::pointer<uint8_t>> recv = sRcvr.GetLastData();
|
||||
|
||||
ASSERT_EQ(recv.size(), 1u);
|
||||
ASSERT_EQ(recv[0].size(), 5u);
|
||||
EXPECT_EQ(std::memcmp(recv[0].get(), "hello", 5), 0);
|
||||
|
||||
clientConn->Disconnect();
|
||||
serverConn->Disconnect();
|
||||
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
376
tests/unit_tests/unix_tunnel/unix_tunnel_connect_tests.cpp
Normal file
376
tests/unit_tests/unix_tunnel/unix_tunnel_connect_tests.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <interfaces/ipc.h>
|
||||
#include <support/interface_ptr.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// Adjust include paths to your tree layout:
|
||||
#include "../sdv_services/uds_unix_sockets/connection.h" // CUnixSocketConnection
|
||||
#include "../sdv_services/uds_unix_tunnel/connection.h" // CUnixTunnelConnection
|
||||
|
||||
// ===================== Test helpers =====================
|
||||
class CTunnelTestReceiver :
|
||||
public sdv::IInterfaceAccess,
|
||||
public sdv::ipc::IDataReceiveCallback,
|
||||
public sdv::ipc::IConnectEventCallback
|
||||
{
|
||||
public:
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// IDataReceiveCallback
|
||||
void ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_lastData = seqData;
|
||||
m_received = true;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
// IConnectEventCallback
|
||||
void SetConnectState(sdv::ipc::EConnectState s) override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_state = s;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
if (m_state == expected)
|
||||
return true;
|
||||
|
||||
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
|
||||
while (m_state != expected)
|
||||
{
|
||||
if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaitForData(uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
return m_cv.wait_for(lk, std::chrono::milliseconds(ms), [&]{ return m_received; });
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> GetLastData() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
return m_lastData;
|
||||
}
|
||||
|
||||
sdv::ipc::EConnectState GetConnectState() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
return m_state;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
|
||||
sdv::ipc::EConnectState m_state { sdv::ipc::EConnectState::uninitialized };
|
||||
sdv::sequence<sdv::pointer<uint8_t>> m_lastData;
|
||||
bool m_received { false };
|
||||
};
|
||||
|
||||
// Small helper similar to MakeRandomSuffix() in uds_connect_tests.cpp
|
||||
static std::string MakeRandomSuffix()
|
||||
{
|
||||
std::mt19937_64 rng{std::random_device{}()};
|
||||
std::uniform_int_distribution<uint64_t> dist;
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << dist(rng);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// ===================== Tests =====================
|
||||
|
||||
// BASIC: server + client CUnixSocketConnection wrapped by CUnixTunnelConnection.
|
||||
// Just test connect + disconnect via tunnel.
|
||||
TEST(UnixTunnelIPC, BasicConnectDisconnectViaTunnel)
|
||||
{
|
||||
const std::string udsPath = std::string("/tmp/sdv_tunnel_") + MakeRandomSuffix() + ".sock";
|
||||
|
||||
// --- Physical transports (UDS) ---
|
||||
auto serverTransport = std::make_shared<CUnixSocketConnection>(
|
||||
-1, /*preconfiguredFd*/
|
||||
true, /*acceptConnectionRequired (server)*/
|
||||
udsPath);
|
||||
|
||||
auto clientTransport = std::make_shared<CUnixSocketConnection>(
|
||||
-1,
|
||||
false, /*client*/
|
||||
udsPath);
|
||||
|
||||
// --- Tunnel wrappers ---
|
||||
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(
|
||||
serverTransport,
|
||||
/*channelId*/ 1);
|
||||
|
||||
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(
|
||||
clientTransport,
|
||||
/*channelId*/ 1);
|
||||
|
||||
CTunnelTestReceiver serverRcvr;
|
||||
CTunnelTestReceiver clientRcvr;
|
||||
|
||||
// Register state callbacks (optional, but useful)
|
||||
uint64_t srvCookie = serverTunnel->RegisterStateEventCallback(&serverRcvr);
|
||||
uint64_t cliCookie = clientTunnel->RegisterStateEventCallback(&clientRcvr);
|
||||
EXPECT_NE(srvCookie, 0u);
|
||||
EXPECT_NE(cliCookie, 0u);
|
||||
|
||||
// Async connect on both ends
|
||||
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
|
||||
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
|
||||
|
||||
// Wait for connections
|
||||
EXPECT_TRUE(serverTunnel->WaitForConnection(5000));
|
||||
EXPECT_TRUE(clientTunnel->WaitForConnection(5000));
|
||||
|
||||
EXPECT_EQ(serverTunnel->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
EXPECT_EQ(clientTunnel->GetConnectState(), sdv::ipc::EConnectState::connected);
|
||||
|
||||
// Disconnect client first
|
||||
clientTunnel->Disconnect();
|
||||
EXPECT_TRUE(clientRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 2000));
|
||||
|
||||
// Disconnect server
|
||||
serverTunnel->Disconnect();
|
||||
EXPECT_TRUE(serverRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 2000));
|
||||
|
||||
serverTunnel->UnregisterStateEventCallback(srvCookie);
|
||||
clientTunnel->UnregisterStateEventCallback(cliCookie);
|
||||
}
|
||||
|
||||
// DATA PATH: send "hello" via tunnel and verify it is received on server side.
|
||||
// For now the tunnel is "pass-through" (no STunnelHeader yet).
|
||||
TEST(UnixTunnelIPC, DataPath_SimpleHello_ViaTunnel)
|
||||
{
|
||||
const std::string udsPath = std::string("/tmp/sdv_tunnel_data_") + MakeRandomSuffix() + ".sock";
|
||||
|
||||
// Physical transports
|
||||
auto serverTransport = std::make_shared<CUnixSocketConnection>(
|
||||
-1, true, udsPath);
|
||||
auto clientTransport = std::make_shared<CUnixSocketConnection>(
|
||||
-1, false, udsPath);
|
||||
|
||||
// Tunnel wrappers (same logical channel for both ends)
|
||||
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(serverTransport, 1);
|
||||
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(clientTransport, 1);
|
||||
|
||||
CTunnelTestReceiver serverRcvr;
|
||||
CTunnelTestReceiver clientRcvr;
|
||||
|
||||
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
|
||||
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
|
||||
|
||||
ASSERT_TRUE(serverTunnel->WaitForConnection(5000));
|
||||
ASSERT_TRUE(clientTunnel->WaitForConnection(5000));
|
||||
|
||||
// Build "hello" payload
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
sdv::pointer<uint8_t> p;
|
||||
const char* msg = "hello";
|
||||
const size_t len = std::strlen(msg);
|
||||
p.resize(len);
|
||||
std::memcpy(p.get(), msg, len);
|
||||
seq.push_back(p);
|
||||
|
||||
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientTunnel.get());
|
||||
ASSERT_NE(pSend, nullptr);
|
||||
|
||||
EXPECT_TRUE(pSend->SendData(seq));
|
||||
|
||||
// Wait for server-side data callback
|
||||
ASSERT_TRUE(serverRcvr.WaitForData(3000));
|
||||
sdv::sequence<sdv::pointer<uint8_t>> recv = serverRcvr.GetLastData();
|
||||
|
||||
ASSERT_EQ(recv.size(), 1u);
|
||||
ASSERT_EQ(recv[0].size(), len);
|
||||
EXPECT_EQ(std::memcmp(recv[0].get(), msg, len), 0);
|
||||
|
||||
clientTunnel->Disconnect();
|
||||
serverTunnel->Disconnect();
|
||||
}
|
||||
|
||||
TEST(UnixTunnelIPC, DataPath_MultiChunk_ViaTunnel)
|
||||
{
|
||||
const std::string udsPath = std::string("/tmp/sdv_tunnel_multi_") + MakeRandomSuffix() + ".sock";
|
||||
|
||||
auto serverTransport = std::make_shared<CUnixSocketConnection>(-1, true, udsPath);
|
||||
auto clientTransport = std::make_shared<CUnixSocketConnection>(-1, false, udsPath);
|
||||
|
||||
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(serverTransport, 1);
|
||||
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(clientTransport, 1);
|
||||
|
||||
CTunnelTestReceiver serverRcvr;
|
||||
CTunnelTestReceiver clientRcvr;
|
||||
|
||||
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
|
||||
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
|
||||
ASSERT_TRUE(serverTunnel->WaitForConnection(5000));
|
||||
ASSERT_TRUE(clientTunnel->WaitForConnection(5000));
|
||||
|
||||
// Two chunks: "sdv" and "framework"
|
||||
sdv::pointer<uint8_t> p1, p2;
|
||||
p1.resize(3);
|
||||
std::memcpy(p1.get(), "sdv", 3);
|
||||
p2.resize(9);
|
||||
std::memcpy(p2.get(), "framework", 9);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(p1);
|
||||
seq.push_back(p2);
|
||||
|
||||
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientTunnel.get());
|
||||
ASSERT_NE(pSend, nullptr);
|
||||
EXPECT_TRUE(pSend->SendData(seq));
|
||||
|
||||
ASSERT_TRUE(serverRcvr.WaitForData(3000));
|
||||
sdv::sequence<sdv::pointer<uint8_t>> recv = serverRcvr.GetLastData();
|
||||
|
||||
ASSERT_EQ(recv.size(), 2u);
|
||||
EXPECT_EQ(recv[0].size(), 3u);
|
||||
EXPECT_EQ(recv[1].size(), 9u);
|
||||
EXPECT_EQ(std::memcmp(recv[0].get(), "sdv", 3), 0);
|
||||
EXPECT_EQ(std::memcmp(recv[1].get(), "framework", 9), 0);
|
||||
|
||||
clientTunnel->Disconnect();
|
||||
serverTunnel->Disconnect();
|
||||
}
|
||||
|
||||
TEST(UnixTunnelIPC, DataPath_LargePayload_Fragmentation_ViaTunnel)
|
||||
{
|
||||
const std::string udsPath = std::string("/tmp/sdv_tunnel_large_") + MakeRandomSuffix() + ".sock";
|
||||
|
||||
auto serverTransport = std::make_shared<CUnixSocketConnection>(-1, true, udsPath);
|
||||
auto clientTransport = std::make_shared<CUnixSocketConnection>(-1, false, udsPath);
|
||||
|
||||
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(serverTransport, 1);
|
||||
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(clientTransport, 1);
|
||||
|
||||
CTunnelTestReceiver serverRcvr;
|
||||
CTunnelTestReceiver clientRcvr;
|
||||
|
||||
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
|
||||
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
|
||||
ASSERT_TRUE(serverTunnel->WaitForConnection(5000));
|
||||
ASSERT_TRUE(clientTunnel->WaitForConnection(5000));
|
||||
|
||||
// Build a large payload (e.g. 256 KiB)
|
||||
const size_t totalBytes = 256 * 1024;
|
||||
sdv::pointer<uint8_t> big;
|
||||
big.resize(totalBytes);
|
||||
for (size_t i = 0; i < totalBytes; ++i)
|
||||
big.get()[i] = static_cast<uint8_t>(i & 0xFF);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(big);
|
||||
|
||||
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientTunnel.get());
|
||||
ASSERT_NE(pSend, nullptr);
|
||||
EXPECT_TRUE(pSend->SendData(seq));
|
||||
|
||||
ASSERT_TRUE(serverRcvr.WaitForData(5000));
|
||||
sdv::sequence<sdv::pointer<uint8_t>> recv = serverRcvr.GetLastData();
|
||||
|
||||
ASSERT_EQ(recv.size(), 1u);
|
||||
ASSERT_EQ(recv[0].size(), totalBytes);
|
||||
EXPECT_EQ(std::memcmp(recv[0].get(), big.get(), totalBytes), 0);
|
||||
|
||||
clientTunnel->Disconnect();
|
||||
serverTunnel->Disconnect();
|
||||
}
|
||||
|
||||
// DATA PATH + HEADER: ensure that the upper receiver sees only the original
|
||||
// payload (no STunnelHeader bytes in front)
|
||||
TEST(UnixTunnelIPC, DataPath_HeaderStripped_ForUpperReceiver)
|
||||
{
|
||||
const std::string udsPath =
|
||||
std::string("/tmp/sdv_tunnel_header_") + MakeRandomSuffix() + ".sock";
|
||||
|
||||
// Physical transports (UDS)
|
||||
auto serverTransport = std::make_shared<CUnixSocketConnection>(
|
||||
-1, /*server*/ true, udsPath);
|
||||
auto clientTransport = std::make_shared<CUnixSocketConnection>(
|
||||
-1, /*client*/ false, udsPath);
|
||||
|
||||
// Tunnel wrappers
|
||||
auto serverTunnel = std::make_shared<CUnixTunnelConnection>(serverTransport, /*channelId*/ 42);
|
||||
auto clientTunnel = std::make_shared<CUnixTunnelConnection>(clientTransport, /*channelId*/ 42);
|
||||
|
||||
CTunnelTestReceiver serverRcvr;
|
||||
CTunnelTestReceiver clientRcvr;
|
||||
|
||||
ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr));
|
||||
ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr));
|
||||
ASSERT_TRUE(serverTunnel->WaitForConnection(5000));
|
||||
ASSERT_TRUE(clientTunnel->WaitForConnection(5000));
|
||||
|
||||
// Build payload "HEADER_TEST"
|
||||
const char* msg = "HEADER_TEST";
|
||||
const size_t len = std::strlen(msg);
|
||||
|
||||
sdv::pointer<uint8_t> p;
|
||||
p.resize(len);
|
||||
std::memcpy(p.get(), msg, len);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(p);
|
||||
|
||||
auto* pSend = dynamic_cast<sdv::ipc::IDataSend*>(clientTunnel.get());
|
||||
ASSERT_NE(pSend, nullptr);
|
||||
EXPECT_TRUE(pSend->SendData(seq));
|
||||
|
||||
// Wait for server-side data callback
|
||||
ASSERT_TRUE(serverRcvr.WaitForData(3000));
|
||||
auto recv = serverRcvr.GetLastData();
|
||||
|
||||
// --- Assertions that header was stripped for upper receiver ---
|
||||
// Upper receiver must see exactly one chunk with the original size, without extra bytes for STunnelHeader
|
||||
ASSERT_EQ(recv.size(), 1u) << "Upper receiver must see exactly one payload chunk";
|
||||
ASSERT_EQ(recv[0].size(), len) << "Payload size must equal original size (no header)";
|
||||
|
||||
EXPECT_EQ(std::memcmp(recv[0].get(), msg, len), 0)
|
||||
<< "Payload content must match exactly the original message (no header in front)";
|
||||
|
||||
clientTunnel->Disconnect();
|
||||
serverTunnel->Disconnect();
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#*******************************************************************************
|
||||
|
||||
if(WIN32)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(WinSocketCommunicationTests LANGUAGES CXX)
|
||||
@@ -36,6 +36,7 @@ target_include_directories(UnitTest_WinSocketConnectTests
|
||||
Ws2_32
|
||||
Winmm
|
||||
Rpcrt4
|
||||
uds_win_sockets
|
||||
)
|
||||
|
||||
add_test(NAME UnitTest_WinSocketConnectTests
|
||||
@@ -50,4 +51,6 @@ add_custom_command(TARGET UnitTest_WinSocketConnectTests POST_BUILD
|
||||
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinSocketConnectTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
@@ -15,8 +15,8 @@
|
||||
#include <support/app_control.h>
|
||||
#include <interfaces/ipc.h>
|
||||
|
||||
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.cpp"
|
||||
#include "../../../sdv_services/uds_win_sockets/connection.cpp"
|
||||
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h"
|
||||
#include "../../../sdv_services/uds_win_sockets/connection.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <afunix.h>
|
||||
@@ -99,7 +99,7 @@ inline void SpinUntilServerArmed(sdv::ipc::IConnect* server, uint32_t maxWaitMs
|
||||
using namespace std::chrono;
|
||||
auto deadline = steady_clock::now() + milliseconds(maxWaitMs);
|
||||
|
||||
while (server->GetStatus() == sdv::ipc::EConnectStatus::uninitialized &&
|
||||
while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized &&
|
||||
steady_clock::now() < deadline)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
@@ -116,7 +116,7 @@ inline void SleepTiny(uint32_t ms = 20)
|
||||
|
||||
} // namespace test_utils
|
||||
|
||||
// Unified test receiver (status + data)
|
||||
// Unified test receiver (state + data)
|
||||
class CTestReceiver :
|
||||
public sdv::IInterfaceAccess,
|
||||
public sdv::ipc::IConnectEventCallback,
|
||||
@@ -128,11 +128,11 @@ public:
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
void SetStatus(sdv::ipc::EConnectStatus s) override
|
||||
void SetConnectState(sdv::ipc::EConnectState s) override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_status = s;
|
||||
m_state = s;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
@@ -147,11 +147,11 @@ public:
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000)
|
||||
bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
|
||||
[&]{ return m_status == expected; });
|
||||
[&]{ return m_state == expected; });
|
||||
}
|
||||
|
||||
bool WaitForData(uint32_t ms = 2000)
|
||||
@@ -171,7 +171,7 @@ private:
|
||||
mutable std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
|
||||
sdv::ipc::EConnectStatus m_status{ sdv::ipc::EConnectStatus::uninitialized };
|
||||
sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized };
|
||||
sdv::sequence<sdv::pointer<uint8_t>> m_data;
|
||||
bool m_hasData{false};
|
||||
};
|
||||
@@ -344,7 +344,7 @@ TEST(WindowsAFUnixIPC, ServerDisconnectPropagates)
|
||||
|
||||
pair.server->Disconnect();
|
||||
|
||||
EXPECT_TRUE(cr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000));
|
||||
EXPECT_TRUE(cr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000));
|
||||
|
||||
pair.client->Disconnect();
|
||||
|
||||
@@ -713,7 +713,8 @@ TEST(WindowsAFUnixIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter)
|
||||
TEST(WindowsAFUnixIPC, ClientTimeout_NoServer)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsChannelMgnt mgr;
|
||||
@@ -742,7 +743,7 @@ TEST(WindowsAFUnixIPC, ClientTimeout_NoServer)
|
||||
EXPECT_FALSE(client->WaitForConnection(1500));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(800));
|
||||
|
||||
EXPECT_EQ(client->GetStatus(), sdv::ipc::EConnectStatus::connection_error);
|
||||
EXPECT_EQ(client->GetConnectState(), sdv::ipc::EConnectState::connection_error);
|
||||
|
||||
client->Disconnect();
|
||||
mgr.Shutdown();
|
||||
@@ -799,7 +800,7 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect)
|
||||
|
||||
t.join();
|
||||
|
||||
EXPECT_TRUE(cr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000));
|
||||
EXPECT_TRUE(cr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000));
|
||||
|
||||
pair.client->Disconnect();
|
||||
mgr.Shutdown();
|
||||
@@ -810,7 +811,8 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect)
|
||||
TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsChannelMgnt mgr;
|
||||
@@ -838,7 +840,7 @@ TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
||||
client->Disconnect();
|
||||
|
||||
EXPECT_EQ(client->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(client->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
@@ -848,7 +850,8 @@ TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup)
|
||||
TEST(WindowsAFUnixIPC, ServerStartThenImmediateDisconnect_NoClient)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsChannelMgnt mgr;
|
||||
@@ -872,14 +875,14 @@ TEST(WindowsAFUnixIPC, ServerStartThenImmediateDisconnect_NoClient)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
||||
server->Disconnect();
|
||||
EXPECT_EQ(server->GetStatus(), sdv::ipc::EConnectStatus::disconnected);
|
||||
EXPECT_EQ(server->GetConnectState(), sdv::ipc::EConnectState::disconnected);
|
||||
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// UnregisterStatusEventCallback: ensure single-listener semantics
|
||||
TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics)
|
||||
// UnregisterStateEventCallback: ensure single-listener semantics
|
||||
TEST(WindowsAFUnixIPC, UnregisterStateEventCallback_SingleListenerSemantics)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
@@ -905,7 +908,7 @@ TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics)
|
||||
ASSERT_NE(client, nullptr);
|
||||
|
||||
CTestReceiver regListener;
|
||||
const uint64_t cookie = server->RegisterStatusEventCallback(®Listener);
|
||||
const uint64_t cookie = server->RegisterStateEventCallback(®Listener);
|
||||
ASSERT_NE(cookie, 0u);
|
||||
|
||||
CTestReceiver mainRecv;
|
||||
@@ -917,15 +920,15 @@ TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics)
|
||||
EXPECT_TRUE(server->WaitForConnection(5000));
|
||||
EXPECT_TRUE(client->WaitForConnection(5000));
|
||||
|
||||
EXPECT_TRUE(mainRecv.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000));
|
||||
EXPECT_FALSE(regListener.WaitForStatus(sdv::ipc::EConnectStatus::connected, 300)) << "The registry listener should NOT receive events while main receiver is active.";
|
||||
EXPECT_TRUE(mainRecv.WaitForState(sdv::ipc::EConnectState::connected, 1000));
|
||||
EXPECT_FALSE(regListener.WaitForState(sdv::ipc::EConnectState::connected, 300)) << "The registry listener should NOT receive events while main receiver is active.";
|
||||
|
||||
server->UnregisterStatusEventCallback(cookie);
|
||||
server->UnregisterStateEventCallback(cookie);
|
||||
|
||||
client->Disconnect();
|
||||
EXPECT_TRUE(mainRecv.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1500));
|
||||
EXPECT_TRUE(mainRecv.WaitForState(sdv::ipc::EConnectState::disconnected, 1500));
|
||||
|
||||
EXPECT_FALSE(regListener.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 300));
|
||||
EXPECT_FALSE(regListener.WaitForState(sdv::ipc::EConnectState::disconnected, 300));
|
||||
|
||||
server->Disconnect();
|
||||
mgr.Shutdown();
|
||||
|
||||
134
tests/unit_tests/win_tunnel/CMakeLists.txt
Normal file
134
tests/unit_tests/win_tunnel/CMakeLists.txt
Normal file
@@ -0,0 +1,134 @@
|
||||
#*******************************************************************************
|
||||
# Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Apache License Version 2.0 which is available at
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Contributors:
|
||||
# Denisa Ros - initial API and implementation
|
||||
#*******************************************************************************
|
||||
if(WIN32)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(WinTunnelCommunicationTests LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Executable: GTest for UDS connect
|
||||
add_executable(UnitTest_WinTunnelConnectTests
|
||||
win_tunnel_connect_tests.cpp
|
||||
)
|
||||
|
||||
target_include_directories(UnitTest_WinTunnelConnectTests
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${PROJECT_SOURCE_DIR}/export
|
||||
${PROJECT_SOURCE_DIR}/export/support
|
||||
${PROJECT_SOURCE_DIR}/export/interfaces
|
||||
${PROJECT_SOURCE_DIR}/sdv_services
|
||||
)
|
||||
|
||||
target_link_libraries(UnitTest_WinTunnelConnectTests
|
||||
PRIVATE
|
||||
GTest::GTest
|
||||
Ws2_32
|
||||
Winmm
|
||||
Rpcrt4
|
||||
uds_win_tunnel
|
||||
uds_win_sockets
|
||||
)
|
||||
|
||||
add_test(NAME UnitTest_WinTunnelConnectTests
|
||||
COMMAND UnitTest_WinTunnelConnectTests)
|
||||
|
||||
add_dependencies(UnitTest_WinTunnelConnectTests dependency_sdv_components)
|
||||
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
add_custom_command(TARGET UnitTest_WinTunnelConnectTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
|
||||
"$<TARGET_FILE:UnitTest_WinTunnelConnectTests>"
|
||||
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelConnectTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(UnitTest_WinTunnelChannelMgntTests
|
||||
win_tunnel_channel_mgnt_tests.cpp
|
||||
)
|
||||
|
||||
target_include_directories(UnitTest_WinTunnelChannelMgntTests
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${PROJECT_SOURCE_DIR}/export
|
||||
${PROJECT_SOURCE_DIR}/export/support
|
||||
${PROJECT_SOURCE_DIR}/export/interfaces
|
||||
${PROJECT_SOURCE_DIR}/sdv_services
|
||||
)
|
||||
|
||||
target_link_libraries(UnitTest_WinTunnelChannelMgntTests
|
||||
PRIVATE
|
||||
GTest::GTest
|
||||
Ws2_32
|
||||
Winmm
|
||||
Rpcrt4
|
||||
uds_win_tunnel
|
||||
uds_win_sockets
|
||||
)
|
||||
|
||||
add_test(NAME UnitTest_WinTunnelChannelMgntTests
|
||||
COMMAND UnitTest_WinTunnelChannelMgntTests)
|
||||
|
||||
add_dependencies(UnitTest_WinTunnelChannelMgntTests dependency_sdv_components)
|
||||
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
add_custom_command(TARGET UnitTest_WinTunnelChannelMgntTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
|
||||
"$<TARGET_FILE:UnitTest_WinTunnelChannelMgntTests>"
|
||||
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelChannelMgntTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Add negative/edge case tests
|
||||
add_executable(UnitTest_WinTunnelNegativeEdgeTests
|
||||
win_tunnel_negative_edge_tests.cpp
|
||||
)
|
||||
|
||||
target_include_directories(UnitTest_WinTunnelNegativeEdgeTests
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${PROJECT_SOURCE_DIR}/export
|
||||
${PROJECT_SOURCE_DIR}/export/support
|
||||
${PROJECT_SOURCE_DIR}/export/interfaces
|
||||
${PROJECT_SOURCE_DIR}/sdv_services
|
||||
)
|
||||
|
||||
target_link_libraries(UnitTest_WinTunnelNegativeEdgeTests
|
||||
PRIVATE
|
||||
GTest::GTest
|
||||
Ws2_32
|
||||
Winmm
|
||||
Rpcrt4
|
||||
uds_win_tunnel
|
||||
uds_win_sockets
|
||||
)
|
||||
|
||||
add_test(NAME UnitTest_WinTunnelNegativeEdgeTests
|
||||
COMMAND UnitTest_WinTunnelNegativeEdgeTests)
|
||||
|
||||
add_dependencies(UnitTest_WinTunnelNegativeEdgeTests dependency_sdv_components)
|
||||
|
||||
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
|
||||
add_custom_command(TARGET UnitTest_WinTunnelNegativeEdgeTests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake
|
||||
"$<TARGET_FILE:UnitTest_WinTunnelNegativeEdgeTests>"
|
||||
--gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelNegativeEdgeTests.xml
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
442
tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp
Normal file
442
tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp
Normal file
@@ -0,0 +1,442 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <support/app_control.h>
|
||||
#include <interfaces/ipc.h>
|
||||
|
||||
#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h"
|
||||
#include "../../../sdv_services/uds_win_tunnel/connection.h"
|
||||
|
||||
// Reuse helpers from uds_win_sockets (path normalization, MakeShortUdsPath)
|
||||
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h"
|
||||
#include "../../../sdv_services/uds_win_sockets/connection.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <windows.h>
|
||||
#include <afunix.h>
|
||||
|
||||
// Helper namespace
|
||||
namespace tunnel_utils
|
||||
{
|
||||
inline std::string Expand(const std::string& in)
|
||||
{
|
||||
if (in.find('%') == std::string::npos)
|
||||
return in;
|
||||
char buf[4096] = {};
|
||||
const DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, sizeof(buf));
|
||||
return (n > 0 && n < sizeof(buf)) ? std::string(buf) : in;
|
||||
}
|
||||
|
||||
inline void EnsureParentDir(const std::string& full)
|
||||
{
|
||||
auto p = full.find_last_of("\\/");
|
||||
if (p == std::string::npos)
|
||||
return;
|
||||
std::string dir = full.substr(0, p);
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
}
|
||||
|
||||
inline std::string MakeShortUdsPath(const char* name)
|
||||
{
|
||||
std::string base = R"(%LOCALAPPDATA%\sdv\)";
|
||||
base = Expand(base);
|
||||
EnsureParentDir(base);
|
||||
return base + name;
|
||||
}
|
||||
|
||||
inline std::string RandomHex()
|
||||
{
|
||||
std::mt19937_64 r{std::random_device{}()};
|
||||
std::uniform_int_distribution<uint64_t> d;
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << d(r);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
inline std::string Unique(const char* prefix)
|
||||
{
|
||||
return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str());
|
||||
}
|
||||
|
||||
inline void SpinUntilServerArmed(sdv::ipc::IConnect* server)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
const auto deadline = steady_clock::now() + milliseconds(500);
|
||||
while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized &&
|
||||
steady_clock::now() < deadline)
|
||||
{
|
||||
std::this_thread::sleep_for(milliseconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tunnel_utils
|
||||
|
||||
using namespace tunnel_utils;
|
||||
|
||||
// Unified receiver (state + data)
|
||||
class CTunnelMgrTestReceiver :
|
||||
public sdv::IInterfaceAccess,
|
||||
public sdv::ipc::IDataReceiveCallback,
|
||||
public sdv::ipc::IConnectEventCallback
|
||||
{
|
||||
public:
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
void SetConnectState(sdv::ipc::EConnectState s) override
|
||||
{
|
||||
{ std::lock_guard<std::mutex> lk(m_mtx); m_state = s; }
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& seq) override
|
||||
{
|
||||
{ std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_last = seq; m_has = true; }
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
bool WaitForState(sdv::ipc::EConnectState s, uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
|
||||
[&]{ return m_state == s; });
|
||||
}
|
||||
|
||||
bool WaitForData(uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
|
||||
[&]{ return m_has; });
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> Data()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
return m_last;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
sdv::ipc::EConnectState m_state{sdv::ipc::EConnectState::uninitialized};
|
||||
sdv::sequence<sdv::pointer<uint8_t>> m_last;
|
||||
bool m_has{false};
|
||||
};
|
||||
|
||||
// Helper to create server + client
|
||||
struct TunnelPair
|
||||
{
|
||||
sdv::TObjectPtr serverObj;
|
||||
sdv::ipc::IConnect* server = nullptr;
|
||||
|
||||
sdv::TObjectPtr clientObj;
|
||||
sdv::ipc::IConnect* client = nullptr;
|
||||
};
|
||||
|
||||
static TunnelPair CreateTunnelPair(CSocketsTunnelChannelMgnt& mgr,
|
||||
const std::string& cs)
|
||||
{
|
||||
TunnelPair out;
|
||||
out.serverObj = mgr.Access(cs);
|
||||
out.server = out.serverObj ?
|
||||
out.serverObj.GetInterface<sdv::ipc::IConnect>() : nullptr;
|
||||
|
||||
out.clientObj = mgr.Access(cs);
|
||||
out.client = out.clientObj ?
|
||||
out.clientObj.GetInterface<sdv::ipc::IConnect>() : nullptr;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// TESTS
|
||||
// Manager instantiate + lifecycle
|
||||
TEST(WinTunnelChannelMgnt, InstantiateAndLifecycle)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
|
||||
EXPECT_NO_THROW(mgr.Initialize(""));
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized);
|
||||
|
||||
mgr.SetOperationMode(sdv::EOperationMode::configuring);
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::configuring);
|
||||
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::running);
|
||||
|
||||
mgr.Shutdown();
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending);
|
||||
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Basic connect/disconnect using manager (server + client)
|
||||
TEST(WinTunnelChannelMgnt, BasicConnectDisconnect)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
|
||||
app.SetRunningMode();
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string uds = Unique("tunnel_mgr_basic");
|
||||
const std::string cs = "proto=tunnel;path=" + uds + ";";
|
||||
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
ASSERT_FALSE(ep.ssConnectString.empty());
|
||||
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelMgrTestReceiver sr, cr;
|
||||
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
|
||||
EXPECT_TRUE(pair.server->WaitForConnection(5000));
|
||||
EXPECT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Simple hello (header stripped)
|
||||
TEST(WinTunnelChannelMgnt, DataPath_SimpleHello_ViaManager)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string cs = "proto=tunnel;path=" + Unique("hello_mgr") + ";";
|
||||
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelMgrTestReceiver sr, cr;
|
||||
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
|
||||
ASSERT_TRUE(pair.server->WaitForConnection(5000));
|
||||
ASSERT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
sdv::pointer<uint8_t> msg;
|
||||
msg.resize(5);
|
||||
memcpy(msg.get(), "hello", 5);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(msg);
|
||||
|
||||
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
|
||||
ASSERT_NE(sender, nullptr);
|
||||
|
||||
EXPECT_TRUE(sender->SendData(seq));
|
||||
ASSERT_TRUE(sr.WaitForData(3000));
|
||||
|
||||
auto recv = sr.Data();
|
||||
ASSERT_EQ(recv.size(), 1u);
|
||||
ASSERT_EQ(recv[0].size(), 5u);
|
||||
EXPECT_EQ(memcmp(recv[0].get(), "hello", 5), 0);
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Multi-chunk
|
||||
TEST(WinTunnelChannelMgnt, DataPath_MultiChunk_ViaManager)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string cs = "proto=tunnel;path=" + Unique("mc_mgr") + ";";
|
||||
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelMgrTestReceiver sr, cr;
|
||||
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
ASSERT_TRUE(pair.server->WaitForConnection(5000));
|
||||
ASSERT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
sdv::pointer<uint8_t> a, b;
|
||||
a.resize(3);
|
||||
memcpy(a.get(), "sdv", 3);
|
||||
b.resize(9);
|
||||
memcpy(b.get(), "framework", 9);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(a);
|
||||
seq.push_back(b);
|
||||
|
||||
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
|
||||
ASSERT_NE(sender, nullptr);
|
||||
|
||||
EXPECT_TRUE(sender->SendData(seq));
|
||||
ASSERT_TRUE(sr.WaitForData(3000));
|
||||
|
||||
auto recv = sr.Data();
|
||||
ASSERT_EQ(recv.size(), 2u);
|
||||
EXPECT_EQ(memcmp(recv[0].get(), "sdv", 3), 0);
|
||||
EXPECT_EQ(memcmp(recv[1].get(), "framework", 9), 0);
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Header stripping invariant
|
||||
TEST(WinTunnelChannelMgnt, HeaderStrippedInvariant)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string cs ="proto=tunnel;path=" + Unique("hdr_mgr") + ";";
|
||||
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelMgrTestReceiver sr, cr;
|
||||
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
|
||||
ASSERT_TRUE(pair.server->WaitForConnection(5000));
|
||||
ASSERT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
const char* msg = "HDR_TEST";
|
||||
const size_t len = strlen(msg);
|
||||
|
||||
sdv::pointer<uint8_t> buf;
|
||||
buf.resize(len);
|
||||
memcpy(buf.get(), msg, len);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(buf);
|
||||
|
||||
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
|
||||
ASSERT_NE(sender, nullptr);
|
||||
|
||||
EXPECT_TRUE(sender->SendData(seq));
|
||||
ASSERT_TRUE(sr.WaitForData(3000));
|
||||
|
||||
auto recv = sr.Data();
|
||||
ASSERT_EQ(recv.size(), 1u);
|
||||
ASSERT_EQ(recv[0].size(), len);
|
||||
EXPECT_EQ(memcmp(recv[0].get(), msg, len), 0);
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Long path normalization
|
||||
TEST(WinTunnelChannelMgnt, CreateEndpoint_LongPath_Normalized)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
std::string veryLong(200, 'A');
|
||||
const std::string raw ="C:\\Users\\" + veryLong + "\\AppData\\Local\\sdv\\tunnel_long_" + RandomHex() + ".sock";
|
||||
const std::string cs = "proto=tunnel;path=" + raw + ";";
|
||||
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
ASSERT_FALSE(ep.ssConnectString.empty());
|
||||
|
||||
// path must be normalized (basename only)
|
||||
EXPECT_NE(ep.ssConnectString.find("path=tunnel_long_"), std::string::npos);
|
||||
|
||||
sdv::TObjectPtr obj = mgr.Access(ep.ssConnectString);
|
||||
auto* server = obj.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(server, nullptr);
|
||||
|
||||
CTunnelMgrTestReceiver sr;
|
||||
server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(server);
|
||||
|
||||
// boot client
|
||||
sdv::TObjectPtr cobj = mgr.Access(ep.ssConnectString);
|
||||
auto* client = cobj.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(client, nullptr);
|
||||
|
||||
CTunnelMgrTestReceiver cr;
|
||||
client->AsyncConnect(&cr);
|
||||
|
||||
EXPECT_TRUE(server->WaitForConnection(5000));
|
||||
EXPECT_TRUE(client->WaitForConnection(5000));
|
||||
|
||||
client->Disconnect();
|
||||
server->Disconnect();
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
454
tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp
Normal file
454
tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
#include "gtest/gtest.h"
|
||||
#include <support/app_control.h>
|
||||
#include <interfaces/ipc.h>
|
||||
|
||||
// Include transport and tunnel manager
|
||||
#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h"
|
||||
#include "../../../sdv_services/uds_win_tunnel/connection.h"
|
||||
|
||||
// Include UDS Windows helpers for path generation
|
||||
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h" // ONLY FOR path helpers (MakeShortUdsPath)
|
||||
#include "../../../sdv_services/uds_win_sockets/connection.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <afunix.h>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// Utility namespace from original tests
|
||||
namespace test_utils
|
||||
{
|
||||
inline std::string Expand(const std::string& in)
|
||||
{
|
||||
if (in.find('%') == std::string::npos)
|
||||
return in;
|
||||
char buf[4096] = {};
|
||||
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, sizeof(buf));
|
||||
return (n > 0 && n < sizeof(buf)) ? std::string(buf) : in;
|
||||
}
|
||||
|
||||
inline void EnsureParentDir(const std::string& fullPath)
|
||||
{
|
||||
auto pos = fullPath.find_last_of("\\/");
|
||||
if (pos == std::string::npos)
|
||||
return;
|
||||
std::string dir = fullPath.substr(0, pos);
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
}
|
||||
|
||||
inline std::string MakeShortUdsPath(const char* name)
|
||||
{
|
||||
std::string base = R"(%LOCALAPPDATA%\sdv\)";
|
||||
base = Expand(base);
|
||||
EnsureParentDir(base);
|
||||
return base + name;
|
||||
}
|
||||
|
||||
inline std::string RandomHex()
|
||||
{
|
||||
std::mt19937_64 rng{std::random_device{}()};
|
||||
std::uniform_int_distribution<uint64_t> dist;
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << dist(rng);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
inline std::string UniqueUds(const char* prefix)
|
||||
{
|
||||
return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str());
|
||||
}
|
||||
|
||||
inline void SpinUntilServerArmed(sdv::ipc::IConnect* server, uint32_t maxWaitMs = 300)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto deadline = steady_clock::now() + milliseconds(maxWaitMs);
|
||||
while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized &&
|
||||
steady_clock::now() < deadline)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
inline void SleepTiny(uint32_t ms = 20)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
}
|
||||
|
||||
} // namespace test_utils
|
||||
|
||||
using namespace test_utils;
|
||||
|
||||
// Unified test receiver (status + payload), identical to UDS tests
|
||||
class CTunnelTestReceiver :
|
||||
public sdv::IInterfaceAccess,
|
||||
public sdv::ipc::IConnectEventCallback,
|
||||
public sdv::ipc::IDataReceiveCallback
|
||||
{
|
||||
public:
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
void SetConnectState(sdv::ipc::EConnectState s) override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_state = s;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>& seq) override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
m_data = seq;
|
||||
m_hasData = true;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
bool WaitForStatus(sdv::ipc::EConnectState expected, uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
|
||||
[&]{ return m_state == expected; });
|
||||
}
|
||||
|
||||
bool WaitForData(uint32_t ms = 2000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
return m_cv.wait_for(lk, std::chrono::milliseconds(ms),
|
||||
[&]{ return m_hasData; });
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> Data()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
sdv::ipc::EConnectState m_state{sdv::ipc::EConnectState::uninitialized};
|
||||
sdv::sequence<sdv::pointer<uint8_t>> m_data;
|
||||
bool m_hasData{false};
|
||||
};
|
||||
|
||||
// Helper to create server + client via manager
|
||||
struct TunnelPair
|
||||
{
|
||||
sdv::TObjectPtr serverObj;
|
||||
sdv::ipc::IConnect* server = nullptr;
|
||||
|
||||
sdv::TObjectPtr clientObj;
|
||||
sdv::ipc::IConnect* client = nullptr;
|
||||
};
|
||||
|
||||
static TunnelPair CreateTunnelPair(CSocketsTunnelChannelMgnt& mgr, const std::string& cs)
|
||||
{
|
||||
TunnelPair out;
|
||||
|
||||
out.serverObj = mgr.Access(cs);
|
||||
out.server = out.serverObj ? out.serverObj.GetInterface<sdv::ipc::IConnect>() : nullptr;
|
||||
|
||||
out.clientObj = mgr.Access(cs);
|
||||
out.client = out.clientObj ? out.clientObj.GetInterface<sdv::ipc::IConnect>() : nullptr;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// TEST SUITE START
|
||||
// Instantiate manager
|
||||
TEST(WinTunnelIPC, InstantiateManager)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
EXPECT_NO_THROW(mgr.Initialize(""));
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized);
|
||||
|
||||
EXPECT_NO_THROW(mgr.Shutdown());
|
||||
EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending);
|
||||
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Basic connect/disconnect
|
||||
TEST(WinTunnelIPC, BasicConnectDisconnect)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
|
||||
app.SetRunningMode();
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string path = UniqueUds("tunnel_basic");
|
||||
const std::string cs = "proto=tunnel;path=" + path + ";";
|
||||
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
ASSERT_FALSE(ep.ssConnectString.empty());
|
||||
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelTestReceiver sr, cr;
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
|
||||
EXPECT_TRUE(pair.server->WaitForConnection(5000));
|
||||
EXPECT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Simple data (hello)
|
||||
TEST(WinTunnelIPC, DataPath_SimpleHello_Tunnel)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string cs = "proto=tunnel;path=" + UniqueUds("hello_t") + ";";
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelTestReceiver sr, cr;
|
||||
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
ASSERT_TRUE(pair.server->WaitForConnection(5000));
|
||||
ASSERT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
// Build payload = "hello"
|
||||
sdv::pointer<uint8_t> p;
|
||||
p.resize(5);
|
||||
memcpy(p.get(), "hello", 5);
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(p);
|
||||
|
||||
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
|
||||
ASSERT_NE(sender, nullptr);
|
||||
EXPECT_TRUE(sender->SendData(seq));
|
||||
|
||||
EXPECT_TRUE(sr.WaitForData(3000));
|
||||
auto recv = sr.Data();
|
||||
|
||||
// header eliminated
|
||||
ASSERT_EQ(recv.size(), 1u);
|
||||
ASSERT_EQ(recv[0].size(), 5u);
|
||||
EXPECT_EQ(memcmp(recv[0].get(), "hello", 5), 0);
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Multi-chunk
|
||||
TEST(WinTunnelIPC, DataPath_MultiChunk_Tunnel)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string cs = "proto=tunnel;path=" + UniqueUds("mc_t") + ";";
|
||||
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelTestReceiver sr, cr;
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
ASSERT_TRUE(pair.server->WaitForConnection(5000));
|
||||
ASSERT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
sdv::pointer<uint8_t> p1, p2;
|
||||
p1.resize(3);
|
||||
memcpy(p1.get(), "sdv", 3);
|
||||
p2.resize(9);
|
||||
memcpy(p2.get(), "framework", 9);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(p1);
|
||||
seq.push_back(p2);
|
||||
|
||||
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
|
||||
ASSERT_NE(sender, nullptr);
|
||||
EXPECT_TRUE(sender->SendData(seq));
|
||||
|
||||
ASSERT_TRUE(sr.WaitForData(3000));
|
||||
auto recv = sr.Data();
|
||||
|
||||
ASSERT_EQ(recv.size(), 2u);
|
||||
EXPECT_EQ(memcmp(recv[0].get(), "sdv", 3), 0);
|
||||
EXPECT_EQ(memcmp(recv[1].get(), "framework", 9), 0);
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Large fragmentation
|
||||
TEST(WinTunnelIPC, DataPath_LargePayload_Tunnel)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string cs = "proto=tunnel;path=" + UniqueUds("big_t") + ";";
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelTestReceiver sr, cr;
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
ASSERT_TRUE(pair.server->WaitForConnection(5000));
|
||||
ASSERT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
const size_t N = 256 * 1024;
|
||||
sdv::pointer<uint8_t> payload;
|
||||
payload.resize(N);
|
||||
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
payload.get()[i] = uint8_t(i & 0xFF);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(payload);
|
||||
|
||||
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
|
||||
ASSERT_NE(sender, nullptr);
|
||||
EXPECT_TRUE(sender->SendData(seq));
|
||||
|
||||
ASSERT_TRUE(sr.WaitForData(5000));
|
||||
auto recv = sr.Data();
|
||||
|
||||
ASSERT_EQ(recv.size(), 1u);
|
||||
ASSERT_EQ(recv[0].size(), N);
|
||||
EXPECT_EQ(memcmp(recv[0].get(), payload.get(), N), 0);
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Header stripping test (tunnel feature)
|
||||
TEST(WinTunnelIPC, DataPath_HeaderStripped_Tunnel)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
|
||||
app.SetRunningMode();
|
||||
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
|
||||
const std::string cs =
|
||||
"proto=tunnel;path=" + UniqueUds("headerstrip") + ";";
|
||||
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto pair = CreateTunnelPair(mgr, ep.ssConnectString);
|
||||
|
||||
ASSERT_NE(pair.server, nullptr);
|
||||
ASSERT_NE(pair.client, nullptr);
|
||||
|
||||
CTunnelTestReceiver sr, cr;
|
||||
|
||||
pair.server->AsyncConnect(&sr);
|
||||
SpinUntilServerArmed(pair.server);
|
||||
|
||||
pair.client->AsyncConnect(&cr);
|
||||
ASSERT_TRUE(pair.server->WaitForConnection(5000));
|
||||
ASSERT_TRUE(pair.client->WaitForConnection(5000));
|
||||
|
||||
const char* msg = "HEADER_TEST";
|
||||
const size_t len = strlen(msg);
|
||||
|
||||
sdv::pointer<uint8_t> p;
|
||||
p.resize(len);
|
||||
memcpy(p.get(), msg, len);
|
||||
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seq;
|
||||
seq.push_back(p);
|
||||
|
||||
auto* sender = dynamic_cast<sdv::ipc::IDataSend*>(pair.client);
|
||||
ASSERT_NE(sender, nullptr);
|
||||
|
||||
EXPECT_TRUE(sender->SendData(seq));
|
||||
ASSERT_TRUE(sr.WaitForData(3000));
|
||||
|
||||
auto recv = sr.Data();
|
||||
|
||||
// check header stripped
|
||||
ASSERT_EQ(recv.size(), 1u);
|
||||
ASSERT_EQ(recv[0].size(), len);
|
||||
EXPECT_EQ(memcmp(recv[0].get(), msg, len), 0);
|
||||
|
||||
pair.client->Disconnect();
|
||||
pair.server->Disconnect();
|
||||
mgr.Shutdown();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
164
tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp
Normal file
164
tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <support/app_control.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h"
|
||||
#include "../../../sdv_services/uds_win_tunnel/connection.h"
|
||||
|
||||
// Include UDS Windows helpers for path generation
|
||||
#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h" // ONLY FOR path helpers (MakeShortUdsPath)
|
||||
#include "../../../sdv_services/uds_win_sockets/connection.h"
|
||||
|
||||
// Helper for unique UDS path
|
||||
template<typename T=void>
|
||||
static std::string UniqueUds(const char* prefix) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%s_%08x.sock", prefix, rand());
|
||||
return std::string("%LOCALAPPDATA%/sdv/") + buf;
|
||||
}
|
||||
|
||||
// Negative: invalid connect string
|
||||
TEST(WinTunnelNegative, InvalidConnectString)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
// Missing proto, missing path
|
||||
auto obj = mgr.Access("role=server;");
|
||||
EXPECT_EQ(obj, nullptr);
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Negative: connect to non-existent server
|
||||
TEST(WinTunnelNegative, ConnectToNonExistentServer)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
std::string cs = "proto=tunnel;path=" + UniqueUds("no_server") + ";";
|
||||
auto obj = mgr.Access(cs);
|
||||
if (!obj) {
|
||||
SUCCEED() << "Client object is nullptr as expected when server does not exist";
|
||||
} else {
|
||||
auto* client = obj->GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(client, nullptr);
|
||||
EXPECT_FALSE(client->WaitForConnection(200));
|
||||
}
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Edge: double disconnect
|
||||
TEST(WinTunnelEdge, DoubleDisconnect)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
std::string cs = "proto=tunnel;path=" + UniqueUds("double_disc") + ";";
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto obj = mgr.Access(ep.ssConnectString);
|
||||
auto* conn = obj->GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(conn, nullptr);
|
||||
conn->Disconnect();
|
||||
// Should not crash or throw
|
||||
EXPECT_NO_THROW(conn->Disconnect());
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Edge: repeated connect/disconnect cycles (recreate endpoint each time)
|
||||
TEST(WinTunnelEdge, RepeatedConnectDisconnect)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
std::string cs = "proto=tunnel;path=" + UniqueUds("repeat") + ";";
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
auto ep = mgr.CreateEndpoint(cs); // recreate endpoint every time
|
||||
sdv::TObjectPtr obj = mgr.Access(ep.ssConnectString);
|
||||
auto* conn = obj.GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(conn, nullptr);
|
||||
conn->AsyncConnect(nullptr);
|
||||
conn->WaitForConnection(200);
|
||||
conn->Disconnect();
|
||||
// obj goes out of scope and cleans up
|
||||
}
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Edge: simultaneous multiple clients
|
||||
TEST(WinTunnelEdge, MultipleClients)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(""));
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
std::string cs = "proto=tunnel;path=" + UniqueUds("multi_client") + ";";
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto obj1 = mgr.Access(ep.ssConnectString);
|
||||
auto obj2 = mgr.Access(ep.ssConnectString);
|
||||
auto* c1 = obj1->GetInterface<sdv::ipc::IConnect>();
|
||||
auto* c2 = obj2->GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(c1, nullptr);
|
||||
ASSERT_NE(c2, nullptr);
|
||||
c1->AsyncConnect(nullptr);
|
||||
c2->AsyncConnect(nullptr);
|
||||
c1->WaitForConnection(500);
|
||||
c2->WaitForConnection(500);
|
||||
c1->Disconnect();
|
||||
c2->Disconnect();
|
||||
app.Shutdown();
|
||||
}
|
||||
|
||||
// Edge: callback throws exception (should not crash)
|
||||
class ThrowingReceiver : public sdv::IInterfaceAccess, public sdv::ipc::IDataReceiveCallback, public sdv::ipc::IConnectEventCallback {
|
||||
public:
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
void SetConnectState(sdv::ipc::EConnectState) override { throw std::runtime_error("SetConnectState fail"); }
|
||||
void ReceiveData(sdv::sequence<sdv::pointer<uint8_t>>&) override { throw std::runtime_error("ReceiveData fail"); }
|
||||
};
|
||||
|
||||
TEST(WinTunnelEdge, CallbackThrows)
|
||||
{
|
||||
sdv::app::CAppControl app;
|
||||
ASSERT_TRUE(app.Startup(R"toml([LogHandler]
|
||||
ViewFilter = "Fatal")toml"));
|
||||
CSocketsTunnelChannelMgnt mgr;
|
||||
mgr.Initialize("");
|
||||
mgr.SetOperationMode(sdv::EOperationMode::running);
|
||||
std::string cs = "proto=tunnel;path=" + UniqueUds("cb_throw") + ";";
|
||||
auto ep = mgr.CreateEndpoint(cs);
|
||||
auto obj = mgr.Access(ep.ssConnectString);
|
||||
auto* conn = obj->GetInterface<sdv::ipc::IConnect>();
|
||||
ASSERT_NE(conn, nullptr);
|
||||
ThrowingReceiver rcv;
|
||||
// Should not crash even if callback throws
|
||||
EXPECT_NO_THROW(conn->AsyncConnect(&rcv));
|
||||
conn->Disconnect();
|
||||
app.Shutdown();
|
||||
}
|
||||
Reference in New Issue
Block a user