Feature/zephyr ztest (#118)

* Leverage (older) embedded unit tests into external unit tests build upon copy of Zephyr's ztest library and CMake.

* Expand top-level CMake build to run external unit tests.

* Expand Zephyr module extension to run external unit tests via west or sanitycheck.

Co-authored-by: Gregory Shue <gregory.shue@legrand.us>
This commit is contained in:
Greg Shue
2020-09-16 05:33:34 -07:00
committed by GitHub
parent a7b2e94cb7
commit 19869dccdb
399 changed files with 21885 additions and 5 deletions
+47
View File
@@ -0,0 +1,47 @@
# 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 "/bacnet/[a-zA-Z_/-]*$" "" TST_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(ZTST_DIR "${TST_DIR}/ztest/src")
string(REGEX REPLACE "/test$" "/src" SRC_DIR ${TST_DIR})
add_compile_definitions(
BIG_ENDIAN=0
CONFIG_ZTEST=1
BACAPP_ALL
)
include_directories(
${SRC_DIR}
${TST_DIR}/ztest/include
)
add_executable(${PROJECT_NAME}
# File(s) under test
${SRC_DIR}/bacnet/rpm.c
# Support files and stubs (pathname alphabetical)
${SRC_DIR}/bacnet/bacapp.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacdevobjpropref.c
${SRC_DIR}/bacnet/bacerror.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/datetime.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/lighting.c
${SRC_DIR}/bacnet/memcopy.c
# Test and test library files
./src/main.c
${ZTST_DIR}/ztest_mock.c
${ZTST_DIR}/ztest.c
)
+525
View File
@@ -0,0 +1,525 @@
/*
* Copyright (c) 2020 Legrand North America, LLC.
*
* SPDX-License-Identifier: MIT
*/
/* @file
* @brief test BACnet integer encode/decode APIs
*/
#include <ztest.h>
#include <bacnet/bacerror.h> /* For bacerror_decode_error_class_and_code() */
#include <bacnet/bacdcode.h>
#include <bacnet/rpm.h>
/* TODO: Refactor from bacapp.c (when TEST is defined) */
/* generic - can be used by other unit tests
returns true if matching or same, false if different */
static bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE *value,
BACNET_APPLICATION_DATA_VALUE *test_value)
{
bool status = false; /*return value */
/* does the tag match? */
if (test_value->tag == value->tag)
status = true;
if (status) {
/* second test for same-ness */
status = false;
/* does the value match? */
switch (test_value->tag) {
#if defined(BACAPP_NULL)
case BACNET_APPLICATION_TAG_NULL:
status = true;
break;
#endif
#if defined(BACAPP_BOOLEAN)
case BACNET_APPLICATION_TAG_BOOLEAN:
if (test_value->type.Boolean == value->type.Boolean)
status = true;
break;
#endif
#if defined(BACAPP_UNSIGNED)
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
if (test_value->type.Unsigned_Int == value->type.Unsigned_Int)
status = true;
break;
#endif
#if defined(BACAPP_SIGNED)
case BACNET_APPLICATION_TAG_SIGNED_INT:
if (test_value->type.Signed_Int == value->type.Signed_Int)
status = true;
break;
#endif
#if defined(BACAPP_REAL)
case BACNET_APPLICATION_TAG_REAL:
if (test_value->type.Real == value->type.Real)
status = true;
break;
#endif
#if defined(BACAPP_DOUBLE)
case BACNET_APPLICATION_TAG_DOUBLE:
if (test_value->type.Double == value->type.Double)
status = true;
break;
#endif
#if defined(BACAPP_ENUMERATED)
case BACNET_APPLICATION_TAG_ENUMERATED:
if (test_value->type.Enumerated == value->type.Enumerated)
status = true;
break;
#endif
#if defined(BACAPP_DATE)
case BACNET_APPLICATION_TAG_DATE:
if (datetime_compare_date(
&test_value->type.Date, &value->type.Date) == 0)
status = true;
break;
#endif
#if defined(BACAPP_TIME)
case BACNET_APPLICATION_TAG_TIME:
if (datetime_compare_time(
&test_value->type.Time, &value->type.Time) == 0)
status = true;
break;
#endif
#if defined(BACAPP_OBJECT_ID)
case BACNET_APPLICATION_TAG_OBJECT_ID:
if ((test_value->type.Object_Id.type ==
value->type.Object_Id.type) &&
(test_value->type.Object_Id.instance ==
value->type.Object_Id.instance)) {
status = true;
}
break;
#endif
#if defined(BACAPP_CHARACTER_STRING)
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
status = characterstring_same(&value->type.Character_String,
&test_value->type.Character_String);
break;
#endif
#if defined(BACAPP_OCTET_STRING)
case BACNET_APPLICATION_TAG_OCTET_STRING:
status = octetstring_value_same(
&value->type.Octet_String, &test_value->type.Octet_String);
break;
#endif
#if defined(BACAPP_BIT_STRING)
case BACNET_APPLICATION_TAG_BIT_STRING:
status = bitstring_same(
&value->type.Bit_String, &test_value->type.Bit_String);
break;
#endif
#if 0 /*TODO: Enable when lighting.c builds cleanly */
#if defined(BACAPP_LIGHTING_COMMAND)
case BACNET_APPLICATION_TAG_LIGHTING_COMMAND:
status = lighting_command_same(&value->type.Lighting_Command,
&test_value->type.Lighting_Command);
break;
#endif
#endif /*TODO: */
default:
status = false;
break;
}
}
return status;
}
/**
* @addtogroup bacnet_tests
* @{
*/
/**
* @brief Test
*/
static int rpm_ack_decode_apdu(uint8_t *apdu,
int apdu_len, /* total length of the apdu */
uint8_t *invoke_id,
uint8_t **service_request,
unsigned *service_request_len)
{
int offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
return -1;
*invoke_id = apdu[1];
if (apdu[2] != SERVICE_CONFIRMED_READ_PROP_MULTIPLE)
return -1;
offset = 3;
if (apdu_len > offset) {
if (service_request)
*service_request = &apdu[offset];
if (service_request_len)
*service_request_len = apdu_len - offset;
}
return offset;
}
static int rpm_decode_apdu(uint8_t *apdu,
unsigned apdu_len,
uint8_t *invoke_id,
uint8_t **service_request,
unsigned *service_request_len)
{
unsigned offset = 0;
if (!apdu)
return -1;
/* optional checking - most likely was already done prior to this call */
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
return -1;
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_READ_PROP_MULTIPLE)
return -1;
offset = 4;
if (apdu_len > offset) {
if (service_request)
*service_request = &apdu[offset];
if (service_request_len)
*service_request_len = apdu_len - offset;
}
return offset;
}
static void testReadPropertyMultiple(void)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int test_len = 0;
int apdu_len = 0;
uint8_t invoke_id = 12;
uint8_t test_invoke_id = 0;
uint8_t *service_request = NULL;
unsigned service_request_len = 0;
BACNET_RPM_DATA rpmdata;
rpmdata.object_type = OBJECT_DEVICE;
rpmdata.object_instance = 0;
rpmdata.object_property = PROP_OBJECT_IDENTIFIER;
rpmdata.array_index = 0;
/* build the RPM - try to make it easy for the Application Layer development
*/
/* IDEA: similar construction, but pass apdu, apdu_len pointer, size of apdu
to let the called function handle the out of space problem that these get
into by returning a boolean of success/failure. It almost needs to use
the keylist library or something similar. Also check case of storing a
backoff point (i.e. save enough room for object_end) */
apdu_len = rpm_encode_apdu_init(&apdu[0], invoke_id);
/* each object has a beginning and an end */
apdu_len +=
rpm_encode_apdu_object_begin(&apdu[apdu_len], OBJECT_DEVICE, 123);
/* then stuff as many properties into it as APDU length will allow */
apdu_len += rpm_encode_apdu_object_property(
&apdu[apdu_len], PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL);
apdu_len += rpm_encode_apdu_object_property(
&apdu[apdu_len], PROP_OBJECT_NAME, BACNET_ARRAY_ALL);
apdu_len += rpm_encode_apdu_object_end(&apdu[apdu_len]);
/* each object has a beginning and an end */
apdu_len +=
rpm_encode_apdu_object_begin(&apdu[apdu_len], OBJECT_ANALOG_INPUT, 33);
apdu_len += rpm_encode_apdu_object_property(
&apdu[apdu_len], PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL);
apdu_len += rpm_encode_apdu_object_property(
&apdu[apdu_len], PROP_ALL, BACNET_ARRAY_ALL);
apdu_len += rpm_encode_apdu_object_end(&apdu[apdu_len]);
zassert_not_equal(apdu_len, 0, NULL);
test_len = rpm_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&service_request, /* will point to the service request in the apdu */
&service_request_len);
zassert_not_equal(test_len, -1, NULL);
zassert_equal(test_invoke_id, invoke_id, NULL);
zassert_not_null(service_request, NULL);
zassert_true(service_request_len > 0, NULL);
test_len =
rpm_decode_object_id(service_request, service_request_len, &rpmdata);
zassert_true(test_len > 0, NULL);
zassert_equal(rpmdata.object_type, OBJECT_DEVICE, NULL);
zassert_equal(rpmdata.object_instance, 123, NULL);
len = test_len;
/* decode the object property portion of the service request */
test_len = rpm_decode_object_property(
&service_request[len], service_request_len - len, &rpmdata);
zassert_true(test_len > 0, NULL);
zassert_equal(rpmdata.object_property, PROP_OBJECT_IDENTIFIER, NULL);
zassert_equal(rpmdata.array_index, BACNET_ARRAY_ALL, NULL);
len += test_len;
test_len = rpm_decode_object_property(
&service_request[len], service_request_len - len, &rpmdata);
zassert_true(test_len > 0, NULL);
zassert_equal(rpmdata.object_property, PROP_OBJECT_NAME, NULL);
zassert_equal(rpmdata.array_index, BACNET_ARRAY_ALL, NULL);
len += test_len;
/* try again - we should fail */
test_len = rpm_decode_object_property(
&service_request[len], service_request_len - len, &rpmdata);
zassert_true(test_len < 0, NULL);
/* is it the end of this object? */
test_len =
rpm_decode_object_end(&service_request[len], service_request_len - len);
zassert_equal(test_len, 1, NULL);
len += test_len;
/* try to decode an object id */
test_len = rpm_decode_object_id(
&service_request[len], service_request_len - len, &rpmdata);
zassert_true(test_len > 0, NULL);
zassert_equal(rpmdata.object_type, OBJECT_ANALOG_INPUT, NULL);
zassert_equal(rpmdata.object_instance, 33, NULL);
len += test_len;
/* decode the object property portion of the service request only */
test_len = rpm_decode_object_property(
&service_request[len], service_request_len - len, &rpmdata);
zassert_true(test_len > 0, NULL);
zassert_equal(rpmdata.object_property, PROP_OBJECT_IDENTIFIER, NULL);
zassert_equal(rpmdata.array_index, BACNET_ARRAY_ALL, NULL);
len += test_len;
test_len = rpm_decode_object_property(
&service_request[len], service_request_len - len, &rpmdata);
zassert_true(test_len > 0, NULL);
zassert_equal(rpmdata.object_property, PROP_ALL, NULL);
zassert_equal(rpmdata.array_index, BACNET_ARRAY_ALL, NULL);
len += test_len;
test_len = rpm_decode_object_property(
&service_request[len], service_request_len - len, &rpmdata);
zassert_true(test_len < 0, NULL);
/* got an error -1, is it the end of this object? */
test_len =
rpm_decode_object_end(&service_request[len], service_request_len - len);
zassert_equal(test_len, 1, NULL);
len += test_len;
zassert_equal(len, service_request_len, NULL);
}
static void testReadPropertyMultipleAck(void)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int test_len = 0;
int apdu_len = 0;
uint8_t invoke_id = 12;
uint8_t test_invoke_id = 0;
uint8_t *service_request = NULL;
unsigned service_request_len = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE;
uint32_t object_instance = 0;
BACNET_PROPERTY_ID object_property = PROP_OBJECT_IDENTIFIER;
uint32_t array_index = 0;
BACNET_APPLICATION_DATA_VALUE application_data[4] = { { 0 } };
BACNET_APPLICATION_DATA_VALUE test_application_data = { 0 };
uint8_t application_data_buffer[MAX_APDU] = { 0 };
int application_data_buffer_len = 0;
BACNET_ERROR_CLASS error_class;
BACNET_ERROR_CODE error_code;
BACNET_RPM_DATA rpmdata;
/* build the RPM - try to make it easy for the
Application Layer development */
/* IDEA: similar construction, but pass apdu, apdu_len pointer,
size of apdu to let the called function handle the out of
space problem that these get into by returning a boolean
of success/failure.
It almost needs to use the keylist library or something similar.
Also check case of storing a backoff point
(i.e. save enough room for object_end) */
apdu_len = rpm_ack_encode_apdu_init(&apdu[0], invoke_id);
/* object beginning */
rpmdata.object_type = OBJECT_DEVICE;
rpmdata.object_instance = 123;
apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], &rpmdata);
/* reply property */
apdu_len += rpm_ack_encode_apdu_object_property(
&apdu[apdu_len], PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL);
/* reply value */
application_data[0].tag = BACNET_APPLICATION_TAG_OBJECT_ID;
application_data[0].type.Object_Id.type = OBJECT_DEVICE;
application_data[0].type.Object_Id.instance = 123;
application_data_buffer_len = bacapp_encode_application_data(
&application_data_buffer[0], &application_data[0]);
apdu_len += rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len],
&application_data_buffer[0], application_data_buffer_len);
/* reply property */
apdu_len += rpm_ack_encode_apdu_object_property(
&apdu[apdu_len], PROP_OBJECT_TYPE, BACNET_ARRAY_ALL);
/* reply value */
application_data[1].tag = BACNET_APPLICATION_TAG_ENUMERATED;
application_data[1].type.Enumerated = OBJECT_DEVICE;
application_data_buffer_len = bacapp_encode_application_data(
&application_data_buffer[0], &application_data[1]);
apdu_len += rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len],
&application_data_buffer[0], application_data_buffer_len);
/* object end */
apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]);
/* object beginning */
rpmdata.object_type = OBJECT_ANALOG_INPUT;
rpmdata.object_instance = 33;
apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], &rpmdata);
/* reply property */
apdu_len += rpm_ack_encode_apdu_object_property(
&apdu[apdu_len], PROP_PRESENT_VALUE, BACNET_ARRAY_ALL);
/* reply value */
application_data[2].tag = BACNET_APPLICATION_TAG_REAL;
application_data[2].type.Real = 0.0;
application_data_buffer_len = bacapp_encode_application_data(
&application_data_buffer[0], &application_data[2]);
apdu_len += rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len],
&application_data_buffer[0], application_data_buffer_len);
/* reply property */
apdu_len += rpm_ack_encode_apdu_object_property(
&apdu[apdu_len], PROP_DEADBAND, BACNET_ARRAY_ALL);
/* reply error */
apdu_len += rpm_ack_encode_apdu_object_property_error(
&apdu[apdu_len], ERROR_CLASS_PROPERTY, ERROR_CODE_UNKNOWN_PROPERTY);
/* object end */
apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]);
zassert_not_equal(apdu_len, 0, NULL);
/****** decode the packet ******/
test_len = rpm_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
&service_request, /* will point to the service request in the apdu */
&service_request_len);
zassert_not_equal(test_len, -1, NULL);
zassert_equal(test_invoke_id, invoke_id, NULL);
zassert_not_null(service_request, NULL);
zassert_true(service_request_len > 0, NULL);
/* the first part should be the first object id */
test_len = rpm_ack_decode_object_id(
service_request, service_request_len, &object_type, &object_instance);
zassert_not_equal(test_len, -1, NULL);
zassert_equal(object_type, OBJECT_DEVICE, NULL);
zassert_equal(object_instance, 123, NULL);
len = test_len;
/* extract the property */
test_len = rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
zassert_equal(object_property, PROP_OBJECT_IDENTIFIER, NULL);
zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
len += test_len;
/* what is the result? An error or a value? */
zassert_true(decode_is_opening_tag_number(&service_request[len], 4), NULL);
len++;
/* decode the object property portion of the service request */
/* note: if this was an array, there could have been
more than one element to decode */
test_len = bacapp_decode_application_data(&service_request[len],
service_request_len - len, &test_application_data);
zassert_true(test_len > 0, NULL);
zassert_true(bacapp_same_value(&application_data[0], &test_application_data), NULL);
len += test_len;
zassert_true(decode_is_closing_tag_number(&service_request[len], 4), NULL);
len++;
/* see if there is another property */
test_len = rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
zassert_not_equal(test_len, -1, NULL);
zassert_equal(object_property, PROP_OBJECT_TYPE, NULL);
zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
len += test_len;
/* what is the result value? */
zassert_true(decode_is_opening_tag_number(&service_request[len], 4), NULL);
len++;
/* decode the object property portion of the service request */
test_len = bacapp_decode_application_data(&service_request[len],
service_request_len - len, &test_application_data);
zassert_true(test_len > 0, NULL);
zassert_true(bacapp_same_value(&application_data[1], &test_application_data), NULL);
len += test_len;
zassert_true(decode_is_closing_tag_number(&service_request[len], 4), NULL);
len++;
/* see if there is another property */
/* this time we should fail */
test_len = rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
zassert_equal(test_len, -1, NULL);
/* see if it is the end of this object */
test_len = rpm_ack_decode_object_end(
&service_request[len], service_request_len - len);
zassert_equal(test_len, 1, NULL);
len += test_len;
/* try to decode another object id */
test_len = rpm_ack_decode_object_id(&service_request[len],
service_request_len - len, &object_type, &object_instance);
zassert_not_equal(test_len, -1, NULL);
zassert_equal(object_type, OBJECT_ANALOG_INPUT, NULL);
zassert_equal(object_instance, 33, NULL);
len += test_len;
/* decode the object property portion of the service request only */
test_len = rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
zassert_not_equal(test_len, -1, NULL);
zassert_equal(object_property, PROP_PRESENT_VALUE, NULL);
zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
len += test_len;
/* what is the result value? */
zassert_true(decode_is_opening_tag_number(&service_request[len], 4), NULL);
len++;
/* decode the object property portion of the service request */
test_len = bacapp_decode_application_data(&service_request[len],
service_request_len - len, &test_application_data);
zassert_true(test_len > 0, NULL);
zassert_true(bacapp_same_value(&application_data[2], &test_application_data), NULL);
len += test_len;
zassert_true(decode_is_closing_tag_number(&service_request[len], 4), NULL);
len++;
/* see if there is another property */
test_len = rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
zassert_not_equal(test_len, -1, NULL);
zassert_equal(object_property, PROP_DEADBAND, NULL);
zassert_equal(array_index, BACNET_ARRAY_ALL, NULL);
len += test_len;
/* what is the result value? */
zassert_true(decode_is_opening_tag_number(&service_request[len], 5), NULL);
len++;
/* it was an error reply */
test_len = bacerror_decode_error_class_and_code(&service_request[len],
service_request_len - len, &error_class, &error_code);
zassert_not_equal(test_len, 0, NULL);
zassert_equal(error_class, ERROR_CLASS_PROPERTY, NULL);
zassert_equal(error_code, ERROR_CODE_UNKNOWN_PROPERTY, NULL);
len += test_len;
zassert_true(decode_is_closing_tag_number(&service_request[len], 5), NULL);
len++;
/* is there another property? */
test_len = rpm_ack_decode_object_property(&service_request[len],
service_request_len - len, &object_property, &array_index);
zassert_equal(test_len, -1, NULL);
/* got an error -1, is it the end of this object? */
test_len = rpm_ack_decode_object_end(
&service_request[len], service_request_len - len);
zassert_equal(test_len, 1, NULL);
len += test_len;
/* check for another object */
test_len = rpm_ack_decode_object_id(&service_request[len],
service_request_len - len, &object_type, &object_instance);
zassert_equal(test_len, 0, NULL);
zassert_equal(len, service_request_len, NULL);
}
/**
* @}
*/
void test_main(void)
{
ztest_test_suite(rpm_tests,
ztest_unit_test(testReadPropertyMultiple),
ztest_unit_test(testReadPropertyMultipleAck)
);
ztest_run_test_suite(rpm_tests);
}