Support routed virtual device backup/restore reinitialization (#1320)
* Support routed virtual device backup restore reinitialization Allow routed virtual devices to accept ReinitializeDevice so Backup and Restore states can be handled per device. Gateway behavior remains unchanged, and DeviceCommunicationControl remains disabled for virtual devices. Virtual-device COLDSTART, WARMSTART, and ACTIVATE_CHANGES requests now return OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED instead of being acknowledged. * Cover routed device backup restore test variants Add routed-device tests for ReinitializeDevice service approval, DCC blocking, virtual-device unsupported reinitialize states, per-device Backup/Restore state, and backup timeout countdown behavior. Build device tests in four compile-time variants so BAC_ROUTING and BACNET_BACKUP_RESTORE are covered in both enabled and disabled combinations. * Keep routed device variant tests in CTest Register each optional device test build as its own CTest build/run pair so BAC_ROUTING and BACNET_BACKUP_RESTORE variants keep the existing fixture-based build-before-run behavior without changing the top-level test CMake file. Constraint: Top-level test/CMakeLists.txt remains unchanged to preserve the existing test registration convention Confidence: high Scope-risk: narrow Tested: cmake -S test -B /tmp/bacnet-stack-test-cmake-pr1320 Tested: ctest --test-dir /tmp/bacnet-stack-test-cmake-pr1320 -R '^test_device(_.*)?$' --output-on-failure -j4 Not-tested: Full repository CTest suite * Ensure Backup/Restore has real configuration-file storage Centralize reinitialize state storage so routed devices and the default device use the same access path. Enforce BACNET_BACKUP_FILE_COUNT as a positive Backup/Restore invariant, while preserving BACNET_BACKUP_RESTORE guards for no-backup builds. Constraint: BACNET_BACKUP_RESTORE performs backup and restore through Configuration_Files[0]. Rejected: Preserve BACNET_BACKUP_FILE_COUNT=0 support | it leaves only state/property handling without a usable backup file. Confidence: high Scope-risk: moderate Directive: Guard no-backup builds with BACNET_BACKUP_RESTORE, not BACNET_BACKUP_FILE_COUNT. Tested: ctest --test-dir test/build -R '^(build_device|test_device|build_device_backup_restore|test_device_backup_restore|build_device_bac_routing|test_device_bac_routing|build_device_bac_routing_backup_restore|test_device_bac_routing_backup_restore)$' --output-on-failure Tested: cmake --build build --target server gateway gateway2 bacbasic Not-tested: Runtime BACnet client backup/restore exchange --------- Signed-off-by: kimhyeongjun <hjun1.kim@samsung.com> Co-authored-by: OmX <omx@oh-my-codex.dev>
This commit is contained in:
@@ -30,7 +30,7 @@ include_directories(
|
||||
${TST_DIR}/ztest/include
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
set(TEST_DEVICE_SOURCES
|
||||
# File(s) under test
|
||||
${SRC_DIR}/bacnet/basic/object/device.c
|
||||
# Support files and stubs (pathname alphabetical)
|
||||
@@ -140,3 +140,65 @@ add_executable(${PROJECT_NAME}
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
${ZTST_DIR}/ztest.c
|
||||
)
|
||||
|
||||
function(configure_device_test_target
|
||||
target_name routing_enabled backup_restore_enabled)
|
||||
if(routing_enabled)
|
||||
target_sources(${target_name} PRIVATE
|
||||
${SRC_DIR}/bacnet/basic/object/gateway/gw_device.c
|
||||
)
|
||||
target_compile_definitions(${target_name} PRIVATE
|
||||
BAC_ROUTING
|
||||
)
|
||||
endif()
|
||||
if(backup_restore_enabled)
|
||||
target_compile_definitions(${target_name} PRIVATE
|
||||
BACNET_BACKUP_RESTORE
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Register a CTest build/run pair for one device test binary.
|
||||
function(
|
||||
add_device_ctest
|
||||
target_name
|
||||
test_name)
|
||||
add_test(NAME build_${test_name} COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}" --config $<CONFIG> --target
|
||||
${target_name})
|
||||
add_test(NAME test_${test_name} COMMAND $<TARGET_FILE:${target_name}>)
|
||||
set_tests_properties(test_${test_name} PROPERTIES FIXTURES_REQUIRED fixture_${test_name})
|
||||
set_tests_properties(build_${test_name} PROPERTIES FIXTURES_SETUP fixture_${test_name})
|
||||
endfunction()
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
${TEST_DEVICE_SOURCES}
|
||||
)
|
||||
configure_device_test_target(${PROJECT_NAME} OFF OFF)
|
||||
|
||||
# Backup/Restore-only test binary.
|
||||
add_executable(${PROJECT_NAME}_backup_restore ${TEST_DEVICE_SOURCES})
|
||||
configure_device_test_target(
|
||||
${PROJECT_NAME}_backup_restore
|
||||
OFF
|
||||
ON)
|
||||
|
||||
# BAC_ROUTING-only test binary.
|
||||
add_executable(${PROJECT_NAME}_bac_routing ${TEST_DEVICE_SOURCES})
|
||||
configure_device_test_target(
|
||||
${PROJECT_NAME}_bac_routing
|
||||
ON
|
||||
OFF)
|
||||
|
||||
# BAC_ROUTING with Backup/Restore test binary.
|
||||
add_executable(${PROJECT_NAME}_bac_routing_backup_restore ${TEST_DEVICE_SOURCES})
|
||||
configure_device_test_target(
|
||||
${PROJECT_NAME}_bac_routing_backup_restore
|
||||
ON
|
||||
ON)
|
||||
|
||||
# CTest pair for the Backup/Restore-only binary.
|
||||
add_device_ctest(${PROJECT_NAME}_backup_restore ${basename}_backup_restore)
|
||||
# CTest pair for the BAC_ROUTING-only binary.
|
||||
add_device_ctest(${PROJECT_NAME}_bac_routing ${basename}_bac_routing)
|
||||
# CTest pair for the BAC_ROUTING with Backup/Restore binary.
|
||||
add_device_ctest(${PROJECT_NAME}_bac_routing_backup_restore ${basename}_bac_routing_backup_restore)
|
||||
|
||||
@@ -91,6 +91,49 @@ static bool Write_Property_Proprietary(BACNET_WRITE_PROPERTY_DATA *data)
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(BAC_ROUTING)
|
||||
/**
|
||||
* @brief Verify routed virtual devices still do not expose DCC.
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(device_tests, test_Routed_Device_DCC_Remains_Blocked)
|
||||
#else
|
||||
static void test_Routed_Device_DCC_Remains_Blocked(void)
|
||||
#endif
|
||||
{
|
||||
BACNET_CHARACTER_STRING object_name = { 0 };
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
uint16_t device_index = 0;
|
||||
int len = 0;
|
||||
bool status = false;
|
||||
|
||||
Device_Init(NULL);
|
||||
Routing_Device_Init(1000);
|
||||
status = characterstring_init_ansi(&object_name, "Virtual Device");
|
||||
zassert_true(status, NULL);
|
||||
device_index = Add_Routed_Device(1001, &object_name, "Virtual Device");
|
||||
zassert_equal(device_index, 1, NULL);
|
||||
|
||||
status = Set_Routed_Device_Object_Index(0);
|
||||
zassert_true(status, NULL);
|
||||
len = Routed_Device_Service_Approval(
|
||||
SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, NULL, 0);
|
||||
zassert_equal(len, 0, NULL);
|
||||
|
||||
status = Set_Routed_Device_Object_Index(device_index);
|
||||
zassert_true(status, NULL);
|
||||
len = Routed_Device_Service_Approval(
|
||||
SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, NULL, 0);
|
||||
zassert_not_equal(len, 0, NULL);
|
||||
|
||||
len = Routed_Device_Service_Approval(
|
||||
SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, apdu, 1);
|
||||
zassert_true(len > 0, NULL);
|
||||
status = Set_Routed_Device_Object_Index(0);
|
||||
zassert_true(status, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ReadProperty handler for this objects proprietary properties.
|
||||
* For the given ReadProperty data, the application_data is loaded
|
||||
@@ -421,6 +464,190 @@ static void testDevice(void)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(BAC_ROUTING)
|
||||
static void test_Routed_Device_Reinitialize_Unsupported_State(
|
||||
BACNET_REINITIALIZED_STATE state)
|
||||
{
|
||||
bool status = false;
|
||||
BACNET_REINITIALIZE_DEVICE_DATA rd_data = { 0 };
|
||||
|
||||
Device_Reinitialize_State_Set(BACNET_REINIT_IDLE);
|
||||
Device_Reinitialize_Password_Set(NULL);
|
||||
rd_data.error_class = ERROR_CLASS_DEVICE;
|
||||
rd_data.error_code = ERROR_CODE_SUCCESS;
|
||||
rd_data.state = state;
|
||||
characterstring_init_ansi(&rd_data.password, NULL);
|
||||
|
||||
status = Device_Reinitialize(&rd_data);
|
||||
zassert_false(status, NULL);
|
||||
zassert_equal(
|
||||
rd_data.error_class, ERROR_CLASS_SERVICES, "error-class=%s",
|
||||
bactext_error_class_name(rd_data.error_class));
|
||||
zassert_equal(
|
||||
rd_data.error_code, ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
|
||||
"error-code=%s", bactext_error_code_name(rd_data.error_code));
|
||||
zassert_equal(Device_Reinitialized_State(), BACNET_REINIT_IDLE, NULL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(device_tests, test_Routed_Device_Reinitialize)
|
||||
#else
|
||||
static void test_Routed_Device_Reinitialize(void)
|
||||
#endif
|
||||
{
|
||||
bool status = false;
|
||||
BACNET_CHARACTER_STRING object_name = { 0 };
|
||||
BACNET_REINITIALIZE_DEVICE_DATA rd_data = { 0 };
|
||||
|
||||
Device_Init(NULL);
|
||||
Routing_Device_Init(100);
|
||||
characterstring_init_ansi(&object_name, "VirtualDevice");
|
||||
zassert_equal(Add_Routed_Device(101, &object_name, "Virtual"), 1, NULL);
|
||||
|
||||
status = Set_Routed_Device_Object_Index(0);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(
|
||||
Routed_Device_Service_Approval(
|
||||
SERVICE_SUPPORTED_REINITIALIZE_DEVICE, 0, NULL, 0),
|
||||
0, NULL);
|
||||
zassert_equal(
|
||||
Routed_Device_Service_Approval(
|
||||
SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, NULL, 0),
|
||||
0, NULL);
|
||||
Device_Reinitialize_State_Set(BACNET_REINIT_IDLE);
|
||||
Device_Reinitialize_Password_Set(NULL);
|
||||
rd_data.error_class = ERROR_CLASS_DEVICE;
|
||||
rd_data.error_code = ERROR_CODE_SUCCESS;
|
||||
rd_data.state = BACNET_REINIT_COLDSTART;
|
||||
characterstring_init_ansi(&rd_data.password, NULL);
|
||||
status = Device_Reinitialize(&rd_data);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(Device_Reinitialized_State(), BACNET_REINIT_COLDSTART, NULL);
|
||||
|
||||
status = Set_Routed_Device_Object_Index(1);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(
|
||||
Routed_Device_Service_Approval(
|
||||
SERVICE_SUPPORTED_REINITIALIZE_DEVICE, 0, NULL, 0),
|
||||
0, NULL);
|
||||
zassert_not_equal(
|
||||
Routed_Device_Service_Approval(
|
||||
SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, NULL, 0),
|
||||
0, NULL);
|
||||
test_Routed_Device_Reinitialize_Unsupported_State(BACNET_REINIT_COLDSTART);
|
||||
test_Routed_Device_Reinitialize_Unsupported_State(BACNET_REINIT_WARMSTART);
|
||||
test_Routed_Device_Reinitialize_Unsupported_State(
|
||||
BACNET_REINIT_ACTIVATE_CHANGES);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BAC_ROUTING) && defined(BACNET_BACKUP_RESTORE)
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(device_tests, test_Routed_Device_Backup_Restore_Independence)
|
||||
#else
|
||||
static void test_Routed_Device_Backup_Restore_Independence(void)
|
||||
#endif
|
||||
{
|
||||
bool status = false;
|
||||
BACNET_CHARACTER_STRING object_name = { 0 };
|
||||
BACNET_REINITIALIZE_DEVICE_DATA rd_data = { 0 };
|
||||
|
||||
Device_Init(NULL);
|
||||
Routing_Device_Init(200);
|
||||
characterstring_init_ansi(&object_name, "VirtualDevice1");
|
||||
zassert_equal(Add_Routed_Device(201, &object_name, "Virtual1"), 1, NULL);
|
||||
characterstring_init_ansi(&object_name, "VirtualDevice2");
|
||||
zassert_equal(Add_Routed_Device(202, &object_name, "Virtual2"), 2, NULL);
|
||||
|
||||
rd_data.error_class = ERROR_CLASS_DEVICE;
|
||||
rd_data.error_code = ERROR_CODE_SUCCESS;
|
||||
rd_data.state = BACNET_REINIT_STARTRESTORE;
|
||||
characterstring_init_ansi(&rd_data.password, NULL);
|
||||
|
||||
Set_Routed_Device_Object_Index(1);
|
||||
Device_Reinitialize_Password_Set(NULL);
|
||||
status = Device_Reinitialize(&rd_data);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(
|
||||
Device_Backup_And_Restore_State(), BACKUP_STATE_PERFORMING_A_RESTORE,
|
||||
NULL);
|
||||
|
||||
rd_data.error_class = ERROR_CLASS_DEVICE;
|
||||
rd_data.error_code = ERROR_CODE_SUCCESS;
|
||||
status = Device_Reinitialize(&rd_data);
|
||||
zassert_false(status, NULL);
|
||||
zassert_equal(rd_data.error_class, ERROR_CLASS_DEVICE, NULL);
|
||||
zassert_equal(
|
||||
rd_data.error_code, ERROR_CODE_CONFIGURATION_IN_PROGRESS, NULL);
|
||||
|
||||
Set_Routed_Device_Object_Index(0);
|
||||
zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL);
|
||||
Set_Routed_Device_Object_Index(2);
|
||||
Device_Reinitialize_Password_Set(NULL);
|
||||
zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL);
|
||||
status = Device_Reinitialize(&rd_data);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(
|
||||
Device_Backup_And_Restore_State(), BACKUP_STATE_PERFORMING_A_RESTORE,
|
||||
NULL);
|
||||
|
||||
Set_Routed_Device_Object_Index(1);
|
||||
rd_data.error_class = ERROR_CLASS_DEVICE;
|
||||
rd_data.error_code = ERROR_CODE_SUCCESS;
|
||||
rd_data.state = BACNET_REINIT_ABORTRESTORE;
|
||||
status = Device_Reinitialize(&rd_data);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL);
|
||||
|
||||
Set_Routed_Device_Object_Index(2);
|
||||
zassert_equal(
|
||||
Device_Backup_And_Restore_State(), BACKUP_STATE_PERFORMING_A_RESTORE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(device_tests, test_Routed_Device_Backup_Countdown_Per_Device)
|
||||
#else
|
||||
static void test_Routed_Device_Backup_Countdown_Per_Device(void)
|
||||
#endif
|
||||
{
|
||||
bool status = false;
|
||||
BACNET_CHARACTER_STRING object_name = { 0 };
|
||||
|
||||
Device_Init(NULL);
|
||||
Routing_Device_Init(300);
|
||||
characterstring_init_ansi(&object_name, "VirtualDevice1");
|
||||
zassert_equal(Add_Routed_Device(301, &object_name, "Virtual1"), 1, NULL);
|
||||
characterstring_init_ansi(&object_name, "VirtualDevice2");
|
||||
zassert_equal(Add_Routed_Device(302, &object_name, "Virtual2"), 2, NULL);
|
||||
|
||||
status = Set_Routed_Device_Object_Index(1);
|
||||
zassert_true(status, NULL);
|
||||
Device_Backup_Failure_Timeout_Set(1);
|
||||
Device_Backup_And_Restore_State_Set(BACKUP_STATE_PERFORMING_A_BACKUP);
|
||||
Device_Backup_Failure_Timeout_Restart();
|
||||
|
||||
status = Set_Routed_Device_Object_Index(2);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL);
|
||||
|
||||
status = Set_Routed_Device_Object_Index(1);
|
||||
zassert_true(status, NULL);
|
||||
Device_Timer(500);
|
||||
zassert_equal(
|
||||
Device_Backup_And_Restore_State(), BACKUP_STATE_PERFORMING_A_BACKUP,
|
||||
NULL);
|
||||
|
||||
Device_Timer(500);
|
||||
zassert_equal(
|
||||
Device_Backup_And_Restore_State(), BACKUP_STATE_BACKUP_FAILURE, NULL);
|
||||
|
||||
status = Set_Routed_Device_Object_Index(2);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -430,9 +657,25 @@ ZTEST_SUITE(device_tests, NULL, NULL, NULL, NULL, NULL);
|
||||
#else
|
||||
void test_main(void)
|
||||
{
|
||||
#if defined(BAC_ROUTING) && defined(BACNET_BACKUP_RESTORE)
|
||||
ztest_test_suite(
|
||||
device_tests, ztest_unit_test(testDevice),
|
||||
ztest_unit_test(test_Device_Data_Sharing),
|
||||
ztest_unit_test(test_Routed_Device_DCC_Remains_Blocked),
|
||||
ztest_unit_test(test_Routed_Device_Reinitialize),
|
||||
ztest_unit_test(test_Routed_Device_Backup_Restore_Independence),
|
||||
ztest_unit_test(test_Routed_Device_Backup_Countdown_Per_Device));
|
||||
#elif defined(BAC_ROUTING)
|
||||
ztest_test_suite(
|
||||
device_tests, ztest_unit_test(testDevice),
|
||||
ztest_unit_test(test_Device_Data_Sharing),
|
||||
ztest_unit_test(test_Routed_Device_DCC_Remains_Blocked),
|
||||
ztest_unit_test(test_Routed_Device_Reinitialize));
|
||||
#else
|
||||
ztest_test_suite(
|
||||
device_tests, ztest_unit_test(testDevice),
|
||||
ztest_unit_test(test_Device_Data_Sharing));
|
||||
#endif
|
||||
|
||||
ztest_run_test_suite(device_tests);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user