Refactor/cov subscriptions encoding decoding test (#1296)
* Add BACnetRecipientProcess type encoding and decoding functions with associated tests * Refactor COV subscription encoding and decoding functions to reduce code size and reuse existing unit tested functions. * Refactor COV subscription handling to simplify error checking
This commit is contained in:
@@ -10,14 +10,16 @@
|
||||
|
||||
void bip_get_my_address(BACNET_ADDRESS *my_address)
|
||||
{
|
||||
(void)my_address;
|
||||
if (my_address) {
|
||||
my_address->mac_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int bip_send_pdu(
|
||||
BACNET_ADDRESS *dest,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *pdu,
|
||||
unsigned pdu_len)
|
||||
uint16_t pdu_len)
|
||||
{
|
||||
(void)dest;
|
||||
(void)npdu_data;
|
||||
|
||||
@@ -46,3 +46,57 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
(void)rpdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Device_Valid_Object_Id(
|
||||
BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
|
||||
{
|
||||
(void)object_type;
|
||||
(void)object_instance;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device_Value_List_Supported(BACNET_OBJECT_TYPE object_type)
|
||||
{
|
||||
(void)object_type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device_COV(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
|
||||
{
|
||||
(void)object_type;
|
||||
(void)object_instance;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Device_COV_Clear(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
|
||||
{
|
||||
(void)object_type;
|
||||
(void)object_instance;
|
||||
}
|
||||
|
||||
bool Device_Encode_Value_List(
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE *value_list)
|
||||
{
|
||||
(void)object_type;
|
||||
(void)object_instance;
|
||||
(void)value_list;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t Routed_Device_Object_Index(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Set_Routed_Device_Object_Index(uint16_t index)
|
||||
{
|
||||
(void)index;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t Get_Num_Managed_Devices(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
|
||||
|
||||
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(test_${basename}
|
||||
VERSION 1.0.0
|
||||
LANGUAGES C)
|
||||
|
||||
string(REGEX REPLACE
|
||||
"/test/bacnet/[a-zA-Z0-9_/-]*$"
|
||||
"/src"
|
||||
SRC_DIR
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
string(REGEX REPLACE
|
||||
"/test/bacnet/[a-zA-Z0-9_/-]*$"
|
||||
"/test"
|
||||
TST_DIR
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
set(ZTST_DIR "${TST_DIR}/ztest/src")
|
||||
|
||||
add_compile_definitions(
|
||||
BACNET_BIG_ENDIAN=0
|
||||
CONFIG_ZTEST=1
|
||||
BACAPP_ALL=1
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${SRC_DIR}
|
||||
${TST_DIR}/ztest/include
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
# File(s) under test
|
||||
${SRC_DIR}/bacnet/basic/service/h_cov.c
|
||||
# Support files and stubs (pathname alphabetical)
|
||||
${SRC_DIR}/bacnet/access_rule.c
|
||||
${SRC_DIR}/bacnet/bacaction.c
|
||||
${SRC_DIR}/bacnet/bacaddr.c
|
||||
${SRC_DIR}/bacnet/bacapp.c
|
||||
${SRC_DIR}/bacnet/bacdcode.c
|
||||
${SRC_DIR}/bacnet/bacdest.c
|
||||
${SRC_DIR}/bacnet/bacdevobjpropref.c
|
||||
${SRC_DIR}/bacnet/bacint.c
|
||||
${SRC_DIR}/bacnet/baclog.c
|
||||
${SRC_DIR}/bacnet/bacreal.c
|
||||
${SRC_DIR}/bacnet/bacstr.c
|
||||
${SRC_DIR}/bacnet/bactext.c
|
||||
${SRC_DIR}/bacnet/bacerror.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
${SRC_DIR}/bacnet/basic/tsm/tsm.c
|
||||
${SRC_DIR}/bacnet/bactimevalue.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/cov.c
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/datetime.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/keylist.c
|
||||
${SRC_DIR}/bacnet/hostnport.c
|
||||
${SRC_DIR}/bacnet/indtext.c
|
||||
${SRC_DIR}/bacnet/lighting.c
|
||||
${SRC_DIR}/bacnet/memcopy.c
|
||||
${SRC_DIR}/bacnet/secure_connect.c
|
||||
${SRC_DIR}/bacnet/shed_level.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/timer_value.c
|
||||
${SRC_DIR}/bacnet/timestamp.c
|
||||
${SRC_DIR}/bacnet/weeklyschedule.c
|
||||
${SRC_DIR}/bacnet/abort.c
|
||||
${SRC_DIR}/bacnet/dcc.c
|
||||
${SRC_DIR}/bacnet/npdu.c
|
||||
${SRC_DIR}/bacnet/reject.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/apdu_mock.c
|
||||
${TST_DIR}/bacnet/basic/object/test/bip_mock.c
|
||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
${ZTST_DIR}/ztest.c
|
||||
)
|
||||
@@ -0,0 +1,348 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief test BACnet COV service handler
|
||||
* @date 2025
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <zephyr/ztest.h>
|
||||
#include <bacnet/bacdef.h>
|
||||
#include <bacnet/cov.h>
|
||||
#include <bacnet/basic/services.h>
|
||||
|
||||
/**
|
||||
* @addtogroup bacnet_tests
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Mock functions for Device_ */
|
||||
#include <bacnet/basic/object/device.h>
|
||||
|
||||
/* Mocks have been moved to bacnet/basic/object/test/ */
|
||||
/**
|
||||
* @brief Test test_h_cov_init_encode
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(h_cov_tests, test_h_cov_init_encode)
|
||||
#else
|
||||
static void test_h_cov_init_encode(void)
|
||||
#endif
|
||||
{
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int apdu_size = sizeof(apdu);
|
||||
int len;
|
||||
uint8_t service_request[128] = { 0 };
|
||||
BACNET_ADDRESS src = { 0 };
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||
|
||||
handler_cov_init();
|
||||
|
||||
len = handler_cov_encode_subscriptions(apdu, apdu_size);
|
||||
/* Should be zero because we initialized the handler */
|
||||
zassert_equal(len, 0, NULL);
|
||||
|
||||
/* Create a valid subscription */
|
||||
cov_data.subscriberProcessIdentifier = 1;
|
||||
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||
cov_data.cancellationRequest = false;
|
||||
cov_data.issueConfirmedNotifications = false;
|
||||
cov_data.lifetime = 0;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
zassert_not_equal(len, 0, NULL);
|
||||
zassert_not_equal(len, BACNET_STATUS_ABORT, NULL);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
|
||||
/* Normal encode */
|
||||
len = handler_cov_encode_subscriptions(apdu, apdu_size);
|
||||
zassert_not_equal(len, 0, NULL);
|
||||
zassert_not_equal(len, BACNET_STATUS_ABORT, NULL);
|
||||
|
||||
/* edge case: exact size buffer encode */
|
||||
len = handler_cov_encode_subscriptions(apdu, len);
|
||||
zassert_equal(len, len, NULL);
|
||||
|
||||
/* edge case: buffer too small */
|
||||
len = handler_cov_encode_subscriptions(apdu, len - 1);
|
||||
zassert_equal(len, BACNET_STATUS_ABORT, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test test_h_cov_timer
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(h_cov_tests, test_h_cov_timer)
|
||||
#else
|
||||
static void test_h_cov_timer(void)
|
||||
#endif
|
||||
{
|
||||
handler_cov_timer_seconds(1);
|
||||
zassert_true(true, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test test_h_cov_fsm
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(h_cov_tests, test_h_cov_fsm)
|
||||
#else
|
||||
static void test_h_cov_fsm(void)
|
||||
#endif
|
||||
{
|
||||
bool idle;
|
||||
uint8_t service_request[128] = { 0 };
|
||||
BACNET_ADDRESS src = { 0 };
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||
int len;
|
||||
|
||||
handler_cov_init();
|
||||
|
||||
idle = handler_cov_fsm();
|
||||
zassert_true(idle, NULL); /* No subscriptions, quickly back to idle */
|
||||
|
||||
/* Subscribe Unconfirmed */
|
||||
cov_data.subscriberProcessIdentifier = 1;
|
||||
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||
cov_data.cancellationRequest = false;
|
||||
cov_data.issueConfirmedNotifications = false;
|
||||
cov_data.lifetime = 0;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
|
||||
/* IDLE -> MARK */
|
||||
idle = handler_cov_fsm();
|
||||
zassert_false(idle, NULL);
|
||||
/* MARK -> CLEAR */
|
||||
idle = handler_cov_fsm();
|
||||
zassert_false(idle, NULL);
|
||||
/* CLEAR -> FREE */
|
||||
idle = handler_cov_fsm();
|
||||
zassert_false(idle, NULL);
|
||||
/* FREE -> SEND */
|
||||
idle = handler_cov_fsm();
|
||||
zassert_false(idle, NULL);
|
||||
/* SEND -> IDLE */
|
||||
idle = handler_cov_fsm();
|
||||
zassert_true(idle, NULL);
|
||||
|
||||
/* Subscribe Confirmed */
|
||||
cov_data.subscriberProcessIdentifier = 2;
|
||||
cov_data.issueConfirmedNotifications = true;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
|
||||
/* Run FSM again */
|
||||
handler_cov_fsm(); /* IDLE -> MARK */
|
||||
handler_cov_fsm(); /* MARK -> CLEAR */
|
||||
handler_cov_fsm(); /* CLEAR -> FREE */
|
||||
handler_cov_fsm(); /* FREE -> SEND */
|
||||
handler_cov_fsm(); /* SEND -> IDLE */
|
||||
|
||||
/* Trigger Confirmed free branch by expiring */
|
||||
cov_data.lifetime = 10;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
|
||||
handler_cov_fsm(); /* IDLE -> MARK */
|
||||
handler_cov_fsm(); /* MARK -> CLEAR */
|
||||
handler_cov_fsm(); /* CLEAR -> FREE */
|
||||
handler_cov_fsm(); /* FREE -> SEND */
|
||||
handler_cov_fsm(); /* SEND -> IDLE */
|
||||
|
||||
/* Expire */
|
||||
handler_cov_timer_seconds(15);
|
||||
|
||||
handler_cov_task();
|
||||
zassert_true(true, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test test_h_cov_subscribe_error_cases
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(h_cov_tests, test_h_cov_subscribe_error_cases)
|
||||
#else
|
||||
static void test_h_cov_subscribe_error_cases(void)
|
||||
#endif
|
||||
{
|
||||
uint8_t service_request[128] = { 0 };
|
||||
BACNET_ADDRESS src = { 0 };
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||
int len;
|
||||
|
||||
handler_cov_init();
|
||||
|
||||
/* test segment abort */
|
||||
service_data.segmented_message = true;
|
||||
handler_cov_subscribe(service_request, 10, &src, &service_data);
|
||||
zassert_true(true, NULL);
|
||||
|
||||
/* test missing required parameter */
|
||||
service_data.segmented_message = false;
|
||||
handler_cov_subscribe(service_request, 0, &src, &service_data);
|
||||
zassert_true(true, NULL);
|
||||
|
||||
/* test bad request decode */
|
||||
handler_cov_subscribe(service_request, 1, &src, &service_data);
|
||||
zassert_true(true, NULL);
|
||||
|
||||
/* test valid decode but object valid (by mock) */
|
||||
cov_data.subscriberProcessIdentifier = 1;
|
||||
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||
cov_data.monitoredObjectIdentifier.instance = 12345;
|
||||
cov_data.cancellationRequest = false;
|
||||
cov_data.issueConfirmedNotifications = false;
|
||||
cov_data.lifetime = 0;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
zassert_true(true, NULL);
|
||||
|
||||
/* test cancellation of non-existing */
|
||||
cov_data.cancellationRequest = true;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
zassert_true(true, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test test_h_cov_subscribe_out_of_space
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(h_cov_tests, test_h_cov_subscribe_out_of_space)
|
||||
#else
|
||||
static void test_h_cov_subscribe_out_of_space(void)
|
||||
#endif
|
||||
{
|
||||
uint8_t service_request[128] = { 0 };
|
||||
BACNET_ADDRESS src = { 0 };
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||
int len;
|
||||
int i;
|
||||
|
||||
handler_cov_init();
|
||||
|
||||
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||
cov_data.cancellationRequest = false;
|
||||
cov_data.issueConfirmedNotifications = false;
|
||||
cov_data.lifetime = 0;
|
||||
|
||||
for (i = 0; i < 130; i++) {
|
||||
cov_data.subscriberProcessIdentifier = i;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
}
|
||||
zassert_true(true, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test test_h_cov_timer_expiration
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(h_cov_tests, test_h_cov_timer_expiration)
|
||||
#else
|
||||
static void test_h_cov_timer_expiration(void)
|
||||
#endif
|
||||
{
|
||||
uint8_t service_request[128] = { 0 };
|
||||
BACNET_ADDRESS src = { 0 };
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||
int len;
|
||||
|
||||
handler_cov_init();
|
||||
|
||||
/* Subscribe with lifetime */
|
||||
cov_data.subscriberProcessIdentifier = 1;
|
||||
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||
cov_data.cancellationRequest = false;
|
||||
cov_data.issueConfirmedNotifications = true;
|
||||
cov_data.lifetime = 10;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
|
||||
/* Check timer decreasing */
|
||||
handler_cov_timer_seconds(5);
|
||||
handler_cov_timer_seconds(10); /* Should expire here */
|
||||
zassert_true(true, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test test_h_cov_address_management
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(h_cov_tests, test_h_cov_address_management)
|
||||
#else
|
||||
static void test_h_cov_address_management(void)
|
||||
#endif
|
||||
{
|
||||
uint8_t service_request[128] = { 0 };
|
||||
BACNET_ADDRESS src = { 0 };
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||
int len;
|
||||
int i;
|
||||
|
||||
handler_cov_init();
|
||||
|
||||
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||
cov_data.cancellationRequest = false;
|
||||
cov_data.issueConfirmedNotifications = false;
|
||||
cov_data.lifetime = 0;
|
||||
|
||||
/* Add subscriptions from 18 different sources */
|
||||
for (i = 0; i < 18; i++) {
|
||||
src.mac_len = 1;
|
||||
src.mac[0] = i + 1;
|
||||
cov_data.subscriberProcessIdentifier = i;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
}
|
||||
|
||||
/* Cancel one, to free an address */
|
||||
src.mac_len = 1;
|
||||
src.mac[0] = 1;
|
||||
cov_data.subscriberProcessIdentifier = 0;
|
||||
cov_data.cancellationRequest = true;
|
||||
len = cov_subscribe_service_request_encode(
|
||||
service_request, sizeof(service_request), &cov_data);
|
||||
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||
|
||||
zassert_true(true, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST_SUITE(h_cov_tests, NULL, NULL, NULL, NULL, NULL);
|
||||
#else
|
||||
void test_main(void)
|
||||
{
|
||||
ztest_test_suite(
|
||||
h_cov_tests, ztest_unit_test(test_h_cov_init_encode),
|
||||
ztest_unit_test(test_h_cov_timer), ztest_unit_test(test_h_cov_fsm),
|
||||
ztest_unit_test(test_h_cov_subscribe_error_cases),
|
||||
ztest_unit_test(test_h_cov_subscribe_out_of_space),
|
||||
ztest_unit_test(test_h_cov_timer_expiration),
|
||||
ztest_unit_test(test_h_cov_address_management));
|
||||
|
||||
ztest_run_test_suite(h_cov_tests);
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user