connection between 2 systems and bug fixes (#14)

This commit is contained in:
tompzf
2026-07-03 14:53:48 +02:00
committed by GitHub
parent 0fb5660d27
commit 46842200f1
284 changed files with 35200 additions and 8265 deletions

View File

@@ -0,0 +1,156 @@
#*******************************************************************************
# 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:
# Erik Verhoeven - initial API and implementation
#*******************************************************************************
# Define project
project (ComponentTest_App_Connect VERSION 1.0 LANGUAGES CXX)
# Find the IDL compiler
# REMARKS The compiler can only be found after it has build. Having both the compiler and the unittest project build, causes an
# error during the scanning phase of CMake.
#find_program(SDVIDL NAMES "sdv_idl_compiler" PATHS "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../../bin/" NO_CACHE)
set (SDVIDL "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../../bin/sdv_idl_compiler")
message("Use IDL compiler: ${SDVIDL}")
# Compile the IDL
add_custom_command(
OUTPUT ${PROJECT_SOURCE_DIR}/generated/test_component.h ${PROJECT_SOURCE_DIR}/generated/ps/proxystub.cpp ${PROJECT_SOURCE_DIR}/generated/serdes/test_component_serdes.h
DEPENDS sdv_idl_compiler
MAIN_DEPENDENCY test_component.idl
COMMENT "Build test_component.idl"
COMMAND ${SDVIDL} "${PROJECT_SOURCE_DIR}/test_component.idl" "-I${PROJECT_SOURCE_DIR}/../../../export" "-O${PROJECT_SOURCE_DIR}/generated"
VERBATIM
)
set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h)
set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/ps/proxystub.cpp)
set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/serdes/test_component_serdes.h)
set_source_files_properties(test_component_device.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h)
set_source_files_properties(test_component_basic_service.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h)
set_source_files_properties(test_component_complex_service.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h)
set_source_files_properties(test_component_system_service.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h)
set_source_files_properties(test_component_utility.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h)
# Create the proxy stub dynamic library
add_library(AppConnect_Component_ps SHARED "test_component_ps.cpp")
set_target_properties(AppConnect_Component_ps PROPERTIES PREFIX "")
set_target_properties(AppConnect_Component_ps PROPERTIES SUFFIX ".sdv")
# Create the device dynamic library
add_library(AppConnect_Component_Device SHARED "test_component_device.cpp")
set_target_properties(AppConnect_Component_Device PROPERTIES PREFIX "")
set_target_properties(AppConnect_Component_Device PROPERTIES SUFFIX ".sdv")
# Create the basic service dynamic library
add_library(AppConnect_Component_BasicService SHARED "test_component_basic_service.cpp")
set_target_properties(AppConnect_Component_BasicService PROPERTIES PREFIX "")
set_target_properties(AppConnect_Component_BasicService PROPERTIES SUFFIX ".sdv")
# Create the complex service dynamic library
add_library(AppConnect_Component_ComplexService SHARED "test_component_complex_service.cpp")
set_target_properties(AppConnect_Component_ComplexService PROPERTIES PREFIX "")
set_target_properties(AppConnect_Component_ComplexService PROPERTIES SUFFIX ".sdv")
# Create the system service dynamic library
add_library(AppConnect_Component_SystemService SHARED "test_component_system_service.cpp")
set_target_properties(AppConnect_Component_SystemService PROPERTIES PREFIX "")
set_target_properties(AppConnect_Component_SystemService PROPERTIES SUFFIX ".sdv")
# Create the utility dynamic library
add_library(AppConnect_Component_Utility SHARED "test_component_utility.cpp")
set_target_properties(AppConnect_Component_Utility PROPERTIES PREFIX "")
set_target_properties(AppConnect_Component_Utility PROPERTIES SUFFIX ".sdv")
# Server instances: Install the device, system, basic and complex service, utility and prox/stub components in server instances
# shared memory: 195 and 196
# UDS: 199 and 200
# Tunnel-UDS: 203 and 204
# Client instances: Install the proxy/stub components
# shared memory: 197 and 198
# UDS: 201 and 202
# Tunnel-UDS: 205 and 206
add_custom_target(AppConnect_Component_Install
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component --instance195 "AppConnect_Component_*.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component --instance196 "AppConnect_Component_*.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component_ps --instance197 "AppConnect_Component_ps.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component_ps --instance198 "AppConnect_Component_ps.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component --instance199 "AppConnect_Component_*.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component --instance200 "AppConnect_Component_*.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component_ps --instance201 "AppConnect_Component_ps.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component_ps --instance202 "AppConnect_Component_ps.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component --instance203 "AppConnect_Component_*.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component --instance204 "AppConnect_Component_*.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component_ps --instance205 "AppConnect_Component_ps.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
COMMAND "$<TARGET_FILE:sdv_packager>" DIRECT_INSTALL AppConnect_Component_ps --instance206 "AppConnect_Component_ps.sdv" "-I$<TARGET_FILE_DIR:AppConnect_Component_ps>" --overwrite
DEPENDS dependency_sdv_components AppConnect_Component_ps AppConnect_Component_Device AppConnect_Component_BasicService AppConnect_Component_ComplexService AppConnect_Component_SystemService AppConnect_Component_Utility
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
# Configure listener for server instance
# Shared memory: 196 (195 is default)
# UDS: 199, 200
# Tunnel-UDS: 203, 204
# Connections for client instances
# Shared memory: 197 and 198
# UDS: 201 and 202
# Tunnel-UDS: 205 and 206
add_custom_target(AppConnect_Communication_Install
COMMAND "$<TARGET_FILE:sdv_control>" LISTENER ADD MySpecialListener DefaultSharedMemory Name=private_channel_196_198 --instance196
COMMAND "$<TARGET_FILE:sdv_control>" CONNECTION ADD DefaultChannelTo195 DefaultSharedMemory Name=DEFAULT_CHANNEL_195 --instance197
COMMAND "$<TARGET_FILE:sdv_control>" CONNECTION ADD MySpecialConnection DefaultSharedMemory Name=private_channel_196_198 --instance198
COMMAND "$<TARGET_FILE:sdv_control>" LISTENER ADD Default unix_domain_sockets Name=DEFAULT_CHANNEL_199 --instance199
COMMAND "$<TARGET_FILE:sdv_control>" LISTENER ADD MySpecialListener unix_domain_sockets Name=private_channel_200_202 --instance200
COMMAND "$<TARGET_FILE:sdv_control>" CONNECTION ADD DefaultChannelTo199 unix_domain_sockets Name=DEFAULT_CHANNEL_199 --instance201
COMMAND "$<TARGET_FILE:sdv_control>" CONNECTION ADD MySpecialConnection unix_domain_sockets Name=private_channel_200_202 --instance202
COMMAND "$<TARGET_FILE:sdv_control>" LISTENER ADD Default unix_domain_sockets_tunnel Name=DEFAULT_CHANNEL_203 --instance203
COMMAND "$<TARGET_FILE:sdv_control>" LISTENER ADD MySpecialListener unix_domain_sockets_tunnel Name=private_channel_204_206 --instance204
COMMAND "$<TARGET_FILE:sdv_control>" CONNECTION ADD DefaultChannelTo203 unix_domain_sockets_tunnel Name=DEFAULT_CHANNEL_203 --instance205
COMMAND "$<TARGET_FILE:sdv_control>" CONNECTION ADD MySpecialConnection unix_domain_sockets_tunnel Name=private_channel_204_206 --instance206
DEPENDS dependency_sdv_components AppConnect_Component_ps AppConnect_Component_Device AppConnect_Component_BasicService AppConnect_Component_ComplexService AppConnect_Component_SystemService AppConnect_Component_Utility
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
# Compile the source code of the server
add_executable(AppConnect_Server
"main_server.cpp")
target_link_libraries(AppConnect_Server ${CMAKE_DL_LIBS})
# Compile the source code
add_executable(ComponentTest_App_Connect
"main.cpp"
"app_connect_tests_shared_mem.cpp"
"app_connect_tests_uds.cpp"
"app_connect_tests_uds_tunnel.cpp"
)
target_link_libraries(ComponentTest_App_Connect ${CMAKE_DL_LIBS} GTest::GTest)
# Add the connect test
add_test(NAME ComponentTest_App_Connect COMMAND ComponentTest_App_Connect)
# Execute the test
add_custom_command(TARGET ComponentTest_App_Connect POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:ComponentTest_App_Connect>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ComponentTest_App_Connect.xml
VERBATIM
)
# Build dependencies
add_dependencies(AppConnect_Server dependency_sdv_components)
add_dependencies(ComponentTest_App_Connect dependency_sdv_components)
add_dependencies(ComponentTest_App_Connect AppConnect_Server)
add_dependencies(ComponentTest_App_Connect AppConnect_Component_Install)
add_dependencies(ComponentTest_App_Connect AppConnect_Communication_Install)
add_dependencies(AppConnect_Component_ps dependency_sdv_components)
add_dependencies(AppConnect_Component_Device dependency_sdv_components)
add_dependencies(AppConnect_Component_BasicService dependency_sdv_components)
add_dependencies(AppConnect_Component_ComplexService dependency_sdv_components)
add_dependencies(AppConnect_Component_SystemService dependency_sdv_components)
add_dependencies(AppConnect_Component_Utility dependency_sdv_components)

View File

@@ -0,0 +1,519 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <gtest/gtest.h>
#include "../../../sdv_services/process_control/process_control.h"
#include "../../../global/ipc_named_mutex.h"
#include "../../../global/exec_dir_helper.h"
#include "generated/test_component.h"
#include <interfaces/app.h>
#include <support/app_control.h>
#include <support/mem_access.h>
#include <support/sdv_core.h>
// The test use the following instance numbers (all configured as main application):
// - instance 195 server without additional listener configured (channel: DEFAULT_CHANNEL_195)
// - instance 196 server with additional listener configured (channels: DEFAULT_CHANNEL_196 and private_channel_196_198)
// - instance 197 client with 195-default connection configured (channel: DEFAULT_CHANNEL_195)
// - instance 198 client with 196-special connection configured (channel: private_channel_196_198)
TEST(AppConnect, ServerStart_DefaultSharedMemory)
{
// The default instance
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 195
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
EXPECT_TRUE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main);
EXPECT_EQ(control.GetInstanceID(), 195u);
// Within the server instance all objects should be accessible.
// NOTE: The complex service is running isolated.
// Test utility
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_TRUE(ptrUtilityHelloObject);
auto pHelloObject = ptrUtilityHelloObject.GetInterface<IHello>();
EXPECT_TRUE(pHelloObject);
if (pHelloObject) ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Utility reports:" << std::endl << ss;
// Test system object
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject) ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
// Test device object - executes SayHello on Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
EXPECT_TRUE(pHelloObject);
if (pHelloObject) ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - executes SayHello on Device, Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject) ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject) ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_TRUE(sStages.bBasicService);
//EXPECT_TRUE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
}
TEST(AppConnect, ServerStart_PrivateSharedMemory)
{
// The default instance
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 196
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
EXPECT_TRUE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main);
EXPECT_EQ(control.GetInstanceID(), 196u);
// Test utility
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_TRUE(ptrUtilityHelloObject);
auto pHelloObject = ptrUtilityHelloObject.GetInterface<IHello>();
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Utility reports:" << std::endl << ss;
// Test system object
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
// Test device object - executes SayHello on Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - executes SayHello on Device, Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
// sStages = {};
// ss.clear();
// pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
// EXPECT_TRUE(pHelloObject);
// if (pHelloObject) ss = pHelloObject->SayHello(sStages);
// EXPECT_FALSE(ss.empty());
// EXPECT_TRUE(sStages.bDevice);
// EXPECT_TRUE(sStages.bBasicService);
// EXPECT_TRUE(sStages.bComplexService);
// EXPECT_TRUE(sStages.bSystemService);
// EXPECT_TRUE(sStages.bUtility);
// std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
}
TEST(AppConnect, ServerApp_Control_SharedMem)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"195"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}
TEST(AppConnect, ClientConnect_DefaultSharedMem)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"195"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Start the second system using the default instance. This simulates a client
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// When startup succeeds an automatic connection to the server 195 should have been made.
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 197
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
// Test utility - not allowed
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_FALSE(ptrUtilityHelloObject);
// Test system object - not allowed BUT CURRENTLY WORKING
sStages = {};
ss.clear();
auto pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
//// Test device object - not allowed BUT CURRENTLY WORKING
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject)
// ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_FALSE(sStages.bBasicService);
//EXPECT_FALSE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
// sStages = {};
// ss.clear();
// pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
// EXPECT_TRUE(pHelloObject);
// if (pHelloObject) ss = pHelloObject->SayHello(sStages);
// EXPECT_FALSE(ss.empty());
// EXPECT_TRUE(sStages.bDevice);
// EXPECT_TRUE(sStages.bBasicService);
// EXPECT_TRUE(sStages.bComplexService);
// EXPECT_TRUE(sStages.bSystemService);
// EXPECT_TRUE(sStages.bUtility);
// std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}
TEST(AppConnect, ClientConnect_PrivateSharedMem)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"196"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Start the second system using the default instance. This simulates a client
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// When startup succeeds an automatic connection to the server 196 should have been made.
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 198
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
// Test utility - not allowed
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_FALSE(ptrUtilityHelloObject);
// Test system object - not allowed BUT CURRENTLY WORKING
sStages = {};
ss.clear();
auto pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
// Test device object - not allowed BUT CURRENTLY WORKING
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject)
// ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_FALSE(sStages.bBasicService);
//EXPECT_FALSE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - executes SayHello on Device, Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject) ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_TRUE(sStages.bBasicService);
//EXPECT_TRUE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}

View File

@@ -0,0 +1,519 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <gtest/gtest.h>
#include "../../../sdv_services/process_control/process_control.h"
#include "../../../global/ipc_named_mutex.h"
#include "../../../global/exec_dir_helper.h"
#include "generated/test_component.h"
#include <interfaces/app.h>
#include <support/app_control.h>
#include <support/mem_access.h>
#include <support/sdv_core.h>
// The test use the following instance numbers (all configured as main application):
// - instance 199 server without additional listener configured (channel: DEFAULT_CHANNEL_199)
// - instance 200 server with additional listener configured (channels: DEFAULT_CHANNEL_200 and private_channel_200_202)
// - instance 201 client with 199-default connection configured (channel: DEFAULT_CHANNEL_199)
// - instance 202 client with 200-special connection configured (channel: private_channel_200_202)
TEST(AppConnect_UDS, ServerStart_DefaultUnixDomainSockets)
{
// The default instance
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 199
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
EXPECT_TRUE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main);
EXPECT_EQ(control.GetInstanceID(), 199u);
// Within the server instance all objects should be accessible.
// NOTE: The complex service is running isolated.
// Test utility
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_TRUE(ptrUtilityHelloObject);
auto pHelloObject = ptrUtilityHelloObject.GetInterface<IHello>();
EXPECT_TRUE(pHelloObject);
if (pHelloObject) ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Utility reports:" << std::endl << ss;
// Test system object
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject) ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
// Test device object - executes SayHello on Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
EXPECT_TRUE(pHelloObject);
if (pHelloObject) ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - executes SayHello on Device, Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject) ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject) ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_TRUE(sStages.bBasicService);
//EXPECT_TRUE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
}
TEST(AppConnect_UDS, ServerStart_PrivateUnixDomainSockets)
{
// The default instance
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 200
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
EXPECT_TRUE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main);
EXPECT_EQ(control.GetInstanceID(), 200u);
// Test utility
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_TRUE(ptrUtilityHelloObject);
auto pHelloObject = ptrUtilityHelloObject.GetInterface<IHello>();
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Utility reports:" << std::endl << ss;
// Test system object
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
// Test device object - executes SayHello on Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - executes SayHello on Device, Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
// sStages = {};
// ss.clear();
// pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
// EXPECT_TRUE(pHelloObject);
// if (pHelloObject) ss = pHelloObject->SayHello(sStages);
// EXPECT_FALSE(ss.empty());
// EXPECT_TRUE(sStages.bDevice);
// EXPECT_TRUE(sStages.bBasicService);
// EXPECT_TRUE(sStages.bComplexService);
// EXPECT_TRUE(sStages.bSystemService);
// EXPECT_TRUE(sStages.bUtility);
// std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
}
TEST(AppConnect_UDS, ServerApp_Control_UnixDomainSockets)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"199"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}
TEST(AppConnect_UDS, ClientConnect_DefaultUnixDomainSockets)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"199"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Start the second system using the default instance. This simulates a client
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// When startup succeeds an automatic connection to the server 199 should have been made.
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 201
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
// Test utility - not allowed
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_FALSE(ptrUtilityHelloObject);
// Test system object - not allowed BUT CURRENTLY WORKING
sStages = {};
ss.clear();
auto pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
//// Test device object - not allowed BUT CURRENTLY WORKING
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject)
// ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_FALSE(sStages.bBasicService);
//EXPECT_FALSE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
// sStages = {};
// ss.clear();
// pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
// EXPECT_TRUE(pHelloObject);
// if (pHelloObject) ss = pHelloObject->SayHello(sStages);
// EXPECT_FALSE(ss.empty());
// EXPECT_TRUE(sStages.bDevice);
// EXPECT_TRUE(sStages.bBasicService);
// EXPECT_TRUE(sStages.bComplexService);
// EXPECT_TRUE(sStages.bSystemService);
// EXPECT_TRUE(sStages.bUtility);
// std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}
TEST(AppConnect_UDS, ClientConnect_PrivateUnixDomainSockets)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"200"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Start the second system using the default instance. This simulates a client
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// When startup succeeds an automatic connection to the server 200 should have been made.
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 202
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
// Test utility - not allowed
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_FALSE(ptrUtilityHelloObject);
// Test system object - not allowed BUT CURRENTLY WORKING
sStages = {};
ss.clear();
auto pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
//// Test device object - not allowed BUT CURRENTLY WORKING
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject)
// ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_FALSE(sStages.bBasicService);
//EXPECT_FALSE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
// sStages = {};
// ss.clear();
// pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
// EXPECT_TRUE(pHelloObject);
// if (pHelloObject) ss = pHelloObject->SayHello(sStages);
// EXPECT_FALSE(ss.empty());
// EXPECT_TRUE(sStages.bDevice);
// EXPECT_TRUE(sStages.bBasicService);
// EXPECT_TRUE(sStages.bComplexService);
// EXPECT_TRUE(sStages.bSystemService);
// EXPECT_TRUE(sStages.bUtility);
// std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}

View File

@@ -0,0 +1,557 @@
/********************************************************************************
* Copyright (c) 2065-2066 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 "../../../sdv_services/process_control/process_control.h"
#include "../../../global/ipc_named_mutex.h"
#include "../../../global/exec_dir_helper.h"
#include "generated/test_component.h"
#include <interfaces/app.h>
#include <support/app_control.h>
#include <support/mem_access.h>
#include <support/sdv_core.h>
#include <fstream>
#include <sstream>
namespace
{
void ExpectConfiguredProvider(uint32_t instanceId, const std::string& configName, bool isListener, const std::string& expectedProvider)
{
auto* pConnections = sdv::core::GetObject<sdv::app::IAppConnections>("AppSettingsService");
if (pConnections)
{
const sdv::u8string cfg = isListener ? pConnections->GetListenerConfig(configName)
: pConnections->GetConnectionConfig(configName);
ASSERT_FALSE(cfg.empty()) << "Missing IPC config '" << configName << "'";
const std::string cfgStr = cfg;
EXPECT_NE(cfgStr.find("Name = \"" + expectedProvider + "\""), std::string::npos)
<< "Unexpected provider config for '" << configName << "':\n" << cfgStr;
return;
}
// In main app mode this service is not guaranteed to be directly accessible via repository object lookup.
// Fallback: validate the installed instance settings file where listener/connection definitions are persisted.
std::filesystem::path installDir;
if (const char* pInstall = std::getenv("SDV_COMPONENT_INSTALL"))
installDir = pInstall;
else
installDir = (GetExecDirectory() / ".." / ".." / "bin").lexically_normal();
const auto settingsPath = installDir / std::to_string(instanceId) / "settings.toml";
std::ifstream settingsStream(settingsPath);
ASSERT_TRUE(settingsStream.is_open()) << "Cannot open settings file: " << settingsPath.generic_u8string();
std::stringstream buffer;
buffer << settingsStream.rdbuf();
const std::string content = buffer.str();
const std::string blockStart = isListener ? "[[Settings.Listener]]" : "[[Settings.Connection]]";
const std::string providerSection = isListener ? "[Settings.Listener.Provider]" : "[Settings.Connection.Provider]";
const std::string expectedName = "Name = \"" + configName + "\"";
const std::string expectedProviderName = "Name = \"" + expectedProvider + "\"";
size_t pos = content.find(blockStart);
while (pos != std::string::npos)
{
const size_t nextPos = content.find(blockStart, pos + blockStart.size());
const std::string block = content.substr(pos, (nextPos == std::string::npos) ? std::string::npos : nextPos - pos);
if (block.find(expectedName) != std::string::npos)
{
EXPECT_NE(block.find(providerSection), std::string::npos)
<< "Missing provider section in config block for '" << configName << "'";
EXPECT_NE(block.find(expectedProviderName), std::string::npos)
<< "Unexpected provider in settings block for '" << configName << "':\n" << block;
return;
}
pos = nextPos;
}
FAIL() << "Missing IPC config block '" << configName << "' in " << settingsPath.generic_u8string();
}
} // namespace
// The test use the following instance numbers (all configured as main application):
// - instance 203 server without additional listener configured (channel: DEFAULT_CHANNEL_203)
// - instance 204 server with additional listener configured (channels: private_channel_204_206)
// - instance 205 client with 203-default connection configured (channel: DEFAULT_CHANNEL_203)
// - instance 206 client with 204-special connection configured (channel: private_channel_204_206)
TEST(AppConnect_UDS_Tunnel, ServerStart_DefaultUDS_Tunnel)
{
// The default instance
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 203
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
EXPECT_TRUE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main);
EXPECT_EQ(control.GetInstanceID(), 203u);
// Test utility
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_TRUE(ptrUtilityHelloObject);
auto pHelloObject = ptrUtilityHelloObject.GetInterface<IHello>();
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Utility reports:" << std::endl << ss;
// Test system object
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
// Test device object - executes SayHello on Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - executes SayHello on Device, Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
}
TEST(AppConnect_UDS_Tunnel, ServerStart_PrivateUDS_Tunnel)
{
// The default instance
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 204
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
EXPECT_TRUE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main);
EXPECT_EQ(control.GetInstanceID(), 204u);
ExpectConfiguredProvider(204u, "MySpecialListener", true, "unix_domain_sockets_tunnel");
// Test utility
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_TRUE(ptrUtilityHelloObject);
auto pHelloObject = ptrUtilityHelloObject.GetInterface<IHello>();
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Utility reports:" << std::endl << ss;
// Test system object
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
// Test device object - executes SayHello on Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - executes SayHello on Device, Utility and System Service
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_TRUE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_TRUE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
// sStages = {};
// ss.clear();
// pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
// EXPECT_TRUE(pHelloObject);
// if (pHelloObject) ss = pHelloObject->SayHello(sStages);
// EXPECT_FALSE(ss.empty());
// EXPECT_TRUE(sStages.bDevice);
// EXPECT_TRUE(sStages.bBasicService);
// EXPECT_TRUE(sStages.bComplexService);
// EXPECT_TRUE(sStages.bSystemService);
// EXPECT_TRUE(sStages.bUtility);
// std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
}
TEST(AppConnect_UDS_Tunnel, ServerApp_Control_UDS_Tunnel)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_tunnel_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"203"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}
TEST(AppConnect_UDS_Tunnel, ClientConnect_DefaultUDS_Tunnel)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_tunnel_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"203"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Start the second system using the default instance. This simulates a client
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// When startup succeeds an automatic connection to the server 203 should have been made.
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 205
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
ExpectConfiguredProvider(205u, "DefaultChannelTo203", false, "unix_domain_sockets_tunnel");
// Test utility - not allowed
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_FALSE(ptrUtilityHelloObject);
// Test system object - not allowed BUT CURRENTLY WORKING
sStages = {};
ss.clear();
auto pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
//// Test device object - not allowed BUT CURRENTLY WORKING
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject)
// ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_FALSE(sStages.bBasicService);
//EXPECT_FALSE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}
TEST(AppConnect_UDS_Tunnel, ClientConnect_PrivateUDS_Tunnel)
{
// The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex
ipc::named_mutex mtx("app_connect_tunnel_test_mutex");
std::unique_lock<ipc::named_mutex> lock(mtx);
// Use the process control class to manage the server process
// Enable access bypass to allow its usage.
CProcessControl procctrl;
procctrl.EnableProcessControlAccessBypass();
procctrl.Initialize(sdv::SObjectInfo());
EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized);
// Execute the server process
sdv::process::TProcessID tServerID = procctrl.Execute(
(GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"204"}, sdv::process::EProcessRights::default_rights);
EXPECT_NE(tServerID, 0u);
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server
EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1));
// Start the second system using the default instance. This simulates a client
sdv::app::CAppControl control;
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// When startup succeeds an automatic connection to the server 204 should have been made.
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 206
[Console]
RedirectMon = true
)toml");
EXPECT_TRUE(bResult);
ExpectConfiguredProvider(206u, "MySpecialConnection", false, "unix_domain_sockets_tunnel");
// Test utility - not allowed
SStages sStages{};
std::string ss;
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
EXPECT_FALSE(ptrUtilityHelloObject);
// Test system object - not allowed BUT CURRENTLY WORKING
sStages = {};
ss.clear();
auto pHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_FALSE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_TRUE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "System service reports:" << std::endl << ss;
//// Test device object - not allowed BUT CURRENTLY WORKING
//sStages = {};
//ss.clear();
//pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
//EXPECT_TRUE(pHelloObject);
//if (pHelloObject)
// ss = pHelloObject->SayHello(sStages);
//EXPECT_FALSE(ss.empty());
//EXPECT_TRUE(sStages.bDevice);
//EXPECT_FALSE(sStages.bBasicService);
//EXPECT_FALSE(sStages.bComplexService);
//EXPECT_TRUE(sStages.bSystemService);
//EXPECT_TRUE(sStages.bUtility);
//std::cout << "Device reports:" << std::endl << ss;
// Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed
sStages = {};
ss.clear();
pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
EXPECT_TRUE(pHelloObject);
if (pHelloObject)
ss = pHelloObject->SayHello(sStages);
EXPECT_FALSE(ss.empty());
EXPECT_FALSE(sStages.bDevice);
EXPECT_TRUE(sStages.bBasicService);
EXPECT_FALSE(sStages.bComplexService);
EXPECT_FALSE(sStages.bSystemService);
EXPECT_FALSE(sStages.bUtility);
std::cout << "Basic service reports:" << std::endl << ss;
//// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service
// sStages = {};
// ss.clear();
// pHelloObject = sdv::core::GetObject<IHello>("TestObject_ComplexHelloService");
// EXPECT_TRUE(pHelloObject);
// if (pHelloObject) ss = pHelloObject->SayHello(sStages);
// EXPECT_FALSE(ss.empty());
// EXPECT_TRUE(sStages.bDevice);
// EXPECT_TRUE(sStages.bBasicService);
// EXPECT_TRUE(sStages.bComplexService);
// EXPECT_TRUE(sStages.bSystemService);
// EXPECT_TRUE(sStages.bUtility);
// std::cout << "Basic service reports:" << std::endl << ss;
control.Shutdown();
EXPECT_FALSE(control.IsRunning());
EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context);
EXPECT_EQ(control.GetInstanceID(), 0u);
// Release the mutex; this should shutdown the server
lock.unlock();
// Wait for 1 second
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Test for the running server - should be terminated
EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000));
// Just in case termination
procctrl.Terminate(tServerID);
// End process control
procctrl.Shutdown();
}

View File

@@ -0,0 +1,31 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <gtest/gtest.h>
#include "../../../global/process_watchdog.h"
#include "../../../sdv_services/process_control/process_control.cpp"
/**
* @brief Main function
*/
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int argc, wchar_t* argv[])
#else
extern "C" int main(int argc, char* argv[])
#endif
{
CProcessWatchdog watchdog;
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,99 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include "../../../global/process_watchdog.h"
#include "../../../global/ipc_named_mutex.h"
#include <atomic>
#include <interfaces/ipc.h>
#include <support/sdv_core.h>
#include <support/app_control.h>
/**
* @brief Main function
*/
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int argc, wchar_t* argv[])
#else
extern "C" int main(int argc, char* argv[])
#endif
{
CProcessWatchdog watchdog;
// Workaround for GCC to make certain that POSIX thread library is loaded before the components are loaded.
// REASON: If the first call to a thread is done in a dynamic library, the application is already classified as single
// threaded and a termination is initiated.
// See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev
// NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and
// leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing.
std::atomic_bool bThreadStarted = false;
std::thread thread = std::thread(
[&]()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
bThreadStarted = true;
});
while (!bThreadStarted)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (thread.joinable())
thread.join();
std::cout << "RuntimeDirectory = " << sdv::app::CAppControl::GetFrameworkRuntimeDirectory().generic_u8string() << std::endl;
std::cout << "InstallDirectory = " << sdv::app::CAppControl::GetComponentInstallDirectory().generic_u8string() << std::endl;
std::cout << "Connect test application" << std::endl;
std::cout << "Copyright (C): 2026 ZF Friedrichshafen AG" << std::endl;
std::cout << "Author: Erik Verhoeven" << std::endl << std::endl;
// Get the instance number from the command line.
if (argc != 2)
{
std::cout << "Missing argument; usage: AppConnect_Client <instance_number>" << std::endl;
return -1;
}
#if defined(_WIN32) && defined(_UNICODE)
size_t nInstance = wcstoul(argv[1], nullptr, 10);
#else
size_t nInstance = atoi(argv[1]);
#endif
std::cout << "Listening for connections on instance #" << nInstance << std::endl;
sdv::app::CAppControl control;
bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = )toml" + std::to_string(nInstance));
if (!bResult)
{
std::cout << "Failed to start system..." << std::endl;
return -2;
}
// Lifetime of the server is managed by the locking of a named mutex. As long as locking doesn't work, the server should run.
// Test every 250ms
ipc::named_mutex mtx("app_connect_test_mutex");
bool bRunning = true;
while (bRunning)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
if (mtx.try_lock())
{
bRunning = false;
mtx.unlock();
}
}
control.Shutdown();
std::cout << "Connect test application terminated... (instance #" << nInstance << ")" << std::endl;
return 0;
}

View File

@@ -0,0 +1,46 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <interfaces/core.idl>
#include <interfaces/process.idl>
/**
* @brief Stages the call might go through.
*/
struct SStages
{
boolean bDevice;
boolean bBasicService;
boolean bComplexService;
boolean bSystemService;
boolean bUtility;
};
/**
* @brief Hello interface
*/
interface IHello
{
/**
* @brief Say hello
* @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The
* basic service calls the device. The device calls the system service and the utility.
* @return The greetings string.
*/
u8string SayHello(inout SStages sStages) const;
/**
* @brief Get the PID of the process the component is running in...
*/
sdv::process::TProcessID GetPID() const;
};

View File

@@ -0,0 +1,72 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <mutex>
#include <iostream>
#include <functional>
#include <thread>
#include <support/component_impl.h>
#include <support/local_service_access.h>
#include "generated/test_component.h"
inline sdv::process::TProcessID GetProcessID()
{
static sdv::process::TProcessID tProcessID = 0;
if (!tProcessID)
{
sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject<sdv::process::IProcessInfo>("ProcessControlService");
if (!pProcessInfo) return 0;
tProcessID = pProcessInfo->GetProcessID();
}
return tProcessID;
}
/**
* @brief Basic hello service
*/
class CBasicHelloService : public sdv::CSdvObject, public IHello
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(IHello)
END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::basic_service)
DECLARE_OBJECT_CLASS_NAME("TestObject_BasicHelloService")
/**
* @brief Say hello. Overload of IHello::SayHello.
* @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The
* basic service calls the device. The device calls the system service and the utility.
* @return The greetings string.
*/
virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override
{
sStages.bBasicService = true;
// Request for the device
auto pHelloObject = sdv::core::GetObject<IHello>("TestObject_HelloDevice");
return "PID#" + std::to_string(GetPID()) + " Hello from basic service\n"
+ std::string(pHelloObject ? pHelloObject->SayHello(sStages) : "");
}
/**
* @brief Get the PID of the process the component is running in... Overload of IHello::GetPID.
*/
sdv::process::TProcessID GetPID() const
{
return GetProcessID();
}
};
DEFINE_SDV_OBJECT(CBasicHelloService)

View File

@@ -0,0 +1,81 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <mutex>
#include <iostream>
#include <functional>
#include <thread>
#include <support/component_impl.h>
#include <support/local_service_access.h>
#include "generated/test_component.h"
inline sdv::process::TProcessID GetProcessID()
{
static sdv::process::TProcessID tProcessID = 0;
if (!tProcessID)
{
sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject<sdv::process::IProcessInfo>("ProcessControlService");
if (!pProcessInfo) return 0;
tProcessID = pProcessInfo->GetProcessID();
}
return tProcessID;
}
/**
* @brief Complex hello service
*/
class CComplexHelloService : public sdv::CSdvObject, public IHello
{
public:
CComplexHelloService()
{
std::cout << "CComplexHelloService constructor" << std::endl;
}
~CComplexHelloService()
{
std::cout << "CComplexHelloService destructor" << std::endl;
}
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(IHello)
END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service)
DECLARE_OBJECT_CLASS_NAME("TestObject_ComplexHelloService")
/**
* @brief Say hello. Overload of IHello::SayHello.
* @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The
* basic service calls the device. The device calls the system service and the utility.
* @return The greetings string.
*/
virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override
{
sStages.bComplexService = true;
// Request for the basic server
auto pHelloObject = sdv::core::GetObject<IHello>("TestObject_BasicHelloService");
return "PID#" + std::to_string(GetPID()) + " Hello from complex service\n" +
std::string(pHelloObject ? pHelloObject->SayHello(sStages) : "");
}
/**
* @brief Get the PID of the process the component is running in... Overload of IHello::GetPID.
*/
sdv::process::TProcessID GetPID() const
{
return GetProcessID();
}
};
DEFINE_SDV_OBJECT(CComplexHelloService)

View File

@@ -0,0 +1,76 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <mutex>
#include <iostream>
#include <functional>
#include <thread>
#include <support/component_impl.h>
#include <support/local_service_access.h>
#include "generated/test_component.h"
inline sdv::process::TProcessID GetProcessID()
{
static sdv::process::TProcessID tProcessID = 0;
if (!tProcessID)
{
sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject<sdv::process::IProcessInfo>("ProcessControlService");
if (!pProcessInfo) return 0;
tProcessID = pProcessInfo->GetProcessID();
}
return tProcessID;
}
/**
* @brief Hello device
*/
class CHelloDevice : public sdv::CSdvObject, public IHello
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(IHello)
END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device)
DECLARE_OBJECT_CLASS_NAME("TestObject_HelloDevice")
/**
* @brief Say hello. Overload of IHello::SayHello.
* @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The
* basic service calls the device. The device calls the system service and the utility.
* @return The greetings string.
*/
virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override
{
sStages.bDevice = true;
// Request for the system service
auto pSystemHelloObject = sdv::core::GetObject<IHello>("TestObject_SystemHelloService");
auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility");
auto pUtilityHelloObject = ptrUtilityHelloObject.GetInterface<IHello>();
return "PID#" + std::to_string(GetPID()) + " Hello from device\n" +
std::string(pSystemHelloObject ? pSystemHelloObject->SayHello(sStages) : "") +
std::string(pUtilityHelloObject ? pUtilityHelloObject->SayHello(sStages) : "");
}
/**
* @brief Get the PID of the process the component is running in... Overload of IHello::GetPID.
*/
sdv::process::TProcessID GetPID() const
{
return GetProcessID();
}
};
DEFINE_SDV_OBJECT(CHelloDevice)

View File

@@ -0,0 +1,14 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include "generated/ps/proxystub.cpp"

View File

@@ -0,0 +1,69 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <mutex>
#include <iostream>
#include <functional>
#include <thread>
#include <support/component_impl.h>
#include <support/local_service_access.h>
#include "generated/test_component.h"
inline sdv::process::TProcessID GetProcessID()
{
static sdv::process::TProcessID tProcessID = 0;
if (!tProcessID)
{
sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject<sdv::process::IProcessInfo>("ProcessControlService");
if (!pProcessInfo) return 0;
tProcessID = pProcessInfo->GetProcessID();
}
return tProcessID;
}
/**
* @brief Hello utlity
*/
class CSystemHelloService : public sdv::CSdvObject, public IHello
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(IHello)
END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
DECLARE_OBJECT_CLASS_NAME("TestObject_SystemHelloService")
/**
* @brief Say hello. Overload of IHello::SayHello.
* @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The
* basic service calls the device. The device calls the system service and the utility.
* @return The greetings string.
*/
virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override
{
sStages.bSystemService = true;
return "PID#" + std::to_string(GetPID()) + " Hello from system service\n";
}
/**
* @brief Get the PID of the process the component is running in... Overload of IHello::GetPID.
*/
sdv::process::TProcessID GetPID() const
{
return GetProcessID();
}
};
DEFINE_SDV_OBJECT(CSystemHelloService)

View File

@@ -0,0 +1,70 @@
/********************************************************************************
* 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:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include <mutex>
#include <iostream>
#include <functional>
#include <thread>
#include <support/component_impl.h>
#include <support/local_service_access.h>
#include "generated/test_component.h"
inline sdv::process::TProcessID GetProcessID()
{
static sdv::process::TProcessID tProcessID = 0;
if (!tProcessID)
{
sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject<sdv::process::IProcessInfo>("ProcessControlService");
if (!pProcessInfo) return 0;
tProcessID = pProcessInfo->GetProcessID();
}
return tProcessID;
}
/**
* @brief Hello utlity
*/
class CHelloUtility : public sdv::CSdvObject, public IHello
{
public:
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(IHello)
END_SDV_INTERFACE_MAP()
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility)
DECLARE_OBJECT_CLASS_NAME("TestObject_HelloUtility")
/**
* @brief Say hello. Overload of IHello::SayHello.
* @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The
* basic service calls the device. The device calls the system service and the utility.
* @return The greetings string.
*/
virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override
{
sStages.bUtility = true;
return "PID#" + std::to_string(GetPID()) + " Hello from basic service\n";
}
/**
* @brief Get the PID of the process the component is running in... Overload of IHello::GetPID.
*/
sdv::process::TProcessID GetPID() const
{
return GetProcessID();
}
};
DEFINE_SDV_OBJECT(CHelloUtility)