From edee1ed9a05c71d4d5beb1cecf731cc82ac725de Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Wed, 10 Jul 2024 13:52:04 +0100 Subject: [PATCH] tests: mgmt: mcumgr: Add enum_mgmt test Adds a test that checks the functionality of this new management group Signed-off-by: Jamie McCrae --- .../mgmt/mcumgr/enum_mgmt/CMakeLists.txt | 16 + tests/subsys/mgmt/mcumgr/enum_mgmt/prj.conf | 18 + tests/subsys/mgmt/mcumgr/enum_mgmt/src/main.c | 818 ++++++++++++++++++ .../mgmt/mcumgr/enum_mgmt/src/smp_test_util.c | 123 +++ .../mgmt/mcumgr/enum_mgmt/src/smp_test_util.h | 31 + .../mgmt/mcumgr/enum_mgmt/testcase.yaml | 31 + 6 files changed, 1037 insertions(+) create mode 100644 tests/subsys/mgmt/mcumgr/enum_mgmt/CMakeLists.txt create mode 100644 tests/subsys/mgmt/mcumgr/enum_mgmt/prj.conf create mode 100644 tests/subsys/mgmt/mcumgr/enum_mgmt/src/main.c create mode 100644 tests/subsys/mgmt/mcumgr/enum_mgmt/src/smp_test_util.c create mode 100644 tests/subsys/mgmt/mcumgr/enum_mgmt/src/smp_test_util.h create mode 100644 tests/subsys/mgmt/mcumgr/enum_mgmt/testcase.yaml diff --git a/tests/subsys/mgmt/mcumgr/enum_mgmt/CMakeLists.txt b/tests/subsys/mgmt/mcumgr/enum_mgmt/CMakeLists.txt new file mode 100644 index 00000000000..da2e291e17f --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/enum_mgmt/CMakeLists.txt @@ -0,0 +1,16 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(enum_mgmt) + +FILE(GLOB app_sources + src/*.c +) + +target_sources(app PRIVATE ${app_sources}) +target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/mgmt/mcumgr/transport/include/mgmt/mcumgr/transport/) diff --git a/tests/subsys/mgmt/mcumgr/enum_mgmt/prj.conf b/tests/subsys/mgmt/mcumgr/enum_mgmt/prj.conf new file mode 100644 index 00000000000..4110ec02d97 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/enum_mgmt/prj.conf @@ -0,0 +1,18 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_ZTEST=y +CONFIG_NET_BUF=y +CONFIG_BASE64=y +CONFIG_ZCBOR=y +CONFIG_CRC=y +CONFIG_MCUMGR=y +CONFIG_MCUMGR_TRANSPORT_DUMMY=y +CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE=1024 +CONFIG_MCUMGR_GRP_ENUM=y +CONFIG_MCUMGR_GRP_ENUM_DETAILS=y +CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS=y +CONFIG_MCUMGR_GRP_ENUM_DETAILS_HOOK=y +CONFIG_ZTEST_STACK_SIZE=3096 diff --git a/tests/subsys/mgmt/mcumgr/enum_mgmt/src/main.c b/tests/subsys/mgmt/mcumgr/enum_mgmt/src/main.c new file mode 100644 index 00000000000..c7ec5b3d18d --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/enum_mgmt/src/main.c @@ -0,0 +1,818 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smp_test_util.h" + +#define LOG_LEVEL LOG_LEVEL_DBG +#include +LOG_MODULE_REGISTER(smp_sample); + +#define SHELL_MGMT_HANDLERS 1 +#define OS_MGMT_HANDLERS 6 +#define ENUM_MGMT_HANDLERS 4 + +#define OS_MGMT_NAME "os mgmt" +#define ENUM_MGMT_NAME "enum mgmt" +#define SHELL_MGMT_NAME "shell mgmt" + +#if defined(CONFIG_MCUMGR_GRP_SHELL) && defined(CONFIG_MCUMGR_GRP_OS) +#define TEST_GROUPS 3 +#elif defined(CONFIG_MCUMGR_GRP_SHELL) || defined(CONFIG_MCUMGR_GRP_OS) +#define TEST_GROUPS 2 +#else +#define TEST_GROUPS 1 +#endif + +#if defined(CONFIG_MCUMGR_GRP_SHELL) && defined(CONFIG_MCUMGR_GRP_OS) +#define FOUND_INDEX_SHELL 2 +#else +#define FOUND_INDEX_SHELL 1 +#endif +#define FOUND_INDEX_OS 1 +#define FOUND_INDEX_ENUM 0 + +#define SMP_RESPONSE_WAIT_TIME 3 +#define ZCBOR_BUFFER_SIZE 128 +#define OUTPUT_BUFFER_SIZE 512 +#define ZCBOR_HISTORY_ARRAY_SIZE 7 + +static struct net_buf *nb; +static bool enum_valid_got; +static bool enum_field_added; +static bool event_invalid_got; +static bool block_access; +static bool add_field; + +struct list_entries { + uint8_t entries; + uint16_t groups[16]; +}; + +struct details_entries { + char expected_name[32]; + uint8_t expected_handlers; + bool expected_test; + + bool matched_name; + bool matched_handlers; + bool matched_test; +}; + +#if defined(CONFIG_MCUMGR_GRP_SHELL) +#define SINGLE_MATCHED_SHELL 0x1 +#else +#define SINGLE_MATCHED_SHELL 0x0 +#endif +#if defined(CONFIG_MCUMGR_GRP_OS) +#define SINGLE_MATCHED_OS 0x2 +#else +#define SINGLE_MATCHED_OS 0x0 +#endif +#define SINGLE_MATCHED_ENUM 0x4 + +#define SINGLE_MATCHED_ALL (SINGLE_MATCHED_SHELL | SINGLE_MATCHED_OS | SINGLE_MATCHED_ENUM) + +static void cleanup_test(void *p) +{ + if (nb != NULL) { + net_buf_unref(nb); + nb = NULL; + } + + enum_valid_got = false; + enum_field_added = false; + event_invalid_got = false; + block_access = false; + add_field = false; +} + +ZTEST(enum_mgmt, test_count) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + uint32_t count_response = 0; + size_t decoded = 0; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("count", zcbor_uint32_decode, &count_response), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0); + + ok = create_enum_mgmt_count_packet(zse, buffer, buffer_out, &buffer_size); + zassert_true(ok, "Expected packet creation to be successful"); + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_ENUM), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, ENUM_MGMT_ID_COUNT, "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 4, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element"); + + /* Ensure the right amount of data was read and that the value matches */ + zassert_equal(count_response, TEST_GROUPS, "Expected data mismatch"); + + /* Clean up test */ + cleanup_test(NULL); +} + +static bool parse_list_entries(zcbor_state_t *state, void *user_data) +{ + uint32_t temp = 0; + uint16_t i = 0; + struct list_entries *entry_data = (struct list_entries *)user_data; + + if (!zcbor_list_start_decode(state)) { + return false; + } + + while (!zcbor_array_at_end(state)) { + if (!zcbor_uint32_decode(state, &temp)) { + return false; + } + + if (i > ARRAY_SIZE(entry_data->groups)) { + return false; + } + + entry_data->groups[i] = (uint16_t)temp; + + ++i; + } + + (void)zcbor_list_end_decode(state); + + entry_data->entries = i; + + return true; +} + +ZTEST(enum_mgmt, test_list) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + size_t decoded = 0; + struct list_entries list_response = { 0 }; + uint8_t i; + bool found_groups[TEST_GROUPS] = { 0 }; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("groups", parse_list_entries, &list_response), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0); + + ok = create_enum_mgmt_list_packet(zse, buffer, buffer_out, &buffer_size); + zassert_true(ok, "Expected packet creation to be successful"); + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_ENUM), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, ENUM_MGMT_ID_LIST, "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 4, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element"); + + /* Ensure the right amount of data was read and that the values match */ + zassert_equal(list_response.entries, TEST_GROUPS, "Expected data mismatch"); + + i = 0; + + while (i < list_response.entries) { + uint8_t index = 0xff; + + if (list_response.groups[i] == MGMT_GROUP_ID_ENUM) { + index = FOUND_INDEX_ENUM; + } +#if defined(CONFIG_MCUMGR_GRP_OS) + if (list_response.groups[i] == MGMT_GROUP_ID_OS) { + index = FOUND_INDEX_OS; + } +#endif +#if defined(CONFIG_MCUMGR_GRP_SHELL) + if (list_response.groups[i] == MGMT_GROUP_ID_SHELL) { + index = FOUND_INDEX_SHELL; + } +#endif + + if (index != 0xff) { + found_groups[index] = true; + } + + ++i; + } + + i = 0; + + while (i < TEST_GROUPS) { + zassert_true(found_groups[i], "Expected group to be found in list"); + ++i; + } + + /* Clean up test */ + cleanup_test(NULL); +} + +ZTEST(enum_mgmt, test_single) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + size_t decoded = 0; + uint8_t i; + uint8_t matched_entries = 0; + uint32_t received_group = 0; + bool received_end = false; + + i = 0; + while (received_end == false) { + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("group", zcbor_uint32_decode, &received_group), + ZCBOR_MAP_DECODE_KEY_DECODER("end", zcbor_simple_decode, &received_end), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 3, buffer, ARRAY_SIZE(buffer), 0); + ok = create_enum_mgmt_single_packet(zse, buffer, buffer_out, &buffer_size, i); + zassert_true(ok, "Expected packet creation to be successful"); + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_ENUM), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, ENUM_MGMT_ID_SINGLE, + "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 7, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), + &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_not_equal(decoded, 0, + "Expected to receive at least 1 decoded zcbor element"); + + if (received_group == MGMT_GROUP_ID_SHELL) { + matched_entries |= SINGLE_MATCHED_SHELL; + } else if (received_group == MGMT_GROUP_ID_OS) { + matched_entries |= SINGLE_MATCHED_OS; + } else if (received_group == MGMT_GROUP_ID_ENUM) { + matched_entries |= SINGLE_MATCHED_ENUM; + } else { + zassert_true(0, "Received unknown group"); + } + + if (matched_entries == SINGLE_MATCHED_ALL) { + zassert_true(received_end, "Expected to have received end"); + zassert_equal(decoded, 2, "Expected to receive 2 decoded zcbor elements"); + } else { + zassert_false(received_end, "Did not expect to receive end"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor elements"); + } + + zassert_true((i <= TEST_GROUPS), "Loop ran too many times"); + + /* Clean up test */ + cleanup_test(NULL); + + ++i; + } + + zassert_equal(matched_entries, SINGLE_MATCHED_ALL, "Received entries mismatch"); +} + +static bool parse_details_entries(zcbor_state_t *state, void *user_data) +{ + uint32_t group = 0; + struct zcbor_string name = { 0 }; + uint32_t handlers = 0; + uint32_t test = 0; + uint16_t i = 0; + struct details_entries *entry_data = (struct details_entries *)user_data; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("group", zcbor_uint32_decode, &group), + ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &name), + ZCBOR_MAP_DECODE_KEY_DECODER("handlers", zcbor_uint32_decode, &handlers), + ZCBOR_MAP_DECODE_KEY_DECODER("test", zcbor_uint32_decode, &test), + }; + + if (!zcbor_list_start_decode(state)) { + return false; + } + + while (!zcbor_array_at_end(state)) { + uint8_t index = 0; + bool ok; + size_t decoded = 0; + + /* Reset */ + group = 0; + name.value = NULL; + name.len = 0; + handlers = 0; + test = 0; + + i = 0; + + while (i < ARRAY_SIZE(output_decode)) { + output_decode[i].found = false; + ++i; + } + + ok = zcbor_map_decode_bulk(state, output_decode, ARRAY_SIZE(output_decode), + &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + + if (group == MGMT_GROUP_ID_ENUM) { + index = FOUND_INDEX_ENUM; + + if (strcmp(name.value, ENUM_MGMT_NAME) == 0) { + entry_data[index].matched_name = true; + } + } +#if defined(CONFIG_MCUMGR_GRP_OS) + else if (group == MGMT_GROUP_ID_OS) { + index = FOUND_INDEX_OS; + + if (strcmp(name.value, OS_MGMT_NAME) == 0) { + entry_data[index].matched_name = true; + } + } +#endif +#if defined(CONFIG_MCUMGR_GRP_SHELL) + else if (group == MGMT_GROUP_ID_SHELL) { + index = FOUND_INDEX_SHELL; + } +#endif + else { + return false; + } + + if (entry_data[index].expected_test) { + zassert_equal(decoded, 4, "Expected to receive 4 decoded zcbor element"); + } else { + zassert_equal(decoded, 3, "Expected to receive 3 decoded zcbor element"); + } + + if (memcmp(name.value, entry_data[index].expected_name, name.len) == 0) { + entry_data[index].matched_name = true; + } + + if (handlers == entry_data[index].expected_handlers) { + entry_data[index].matched_handlers = true; + } + + if (output_decode[3].found == entry_data[index].expected_test) { + /* Check value is correct */ + if (entry_data[index].expected_test == false || + (entry_data[index].expected_test == true && test == (group * 3 + 1))) { + entry_data[index].matched_test = true; + } + } + } + + (void)zcbor_list_end_decode(state); + + return true; +} + +ZTEST(enum_mgmt, test_details) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + size_t decoded = 0; + struct details_entries details_response[TEST_GROUPS] = { 0 }; + uint8_t i; + +#if defined(CONFIG_MCUMGR_GRP_SHELL) + strcpy(details_response[FOUND_INDEX_SHELL].expected_name, SHELL_MGMT_NAME); + details_response[FOUND_INDEX_SHELL].expected_handlers = SHELL_MGMT_HANDLERS; + details_response[FOUND_INDEX_SHELL].expected_test = false; +#endif + +#if defined(CONFIG_MCUMGR_GRP_OS) + strcpy(details_response[FOUND_INDEX_OS].expected_name, OS_MGMT_NAME); + details_response[FOUND_INDEX_OS].expected_handlers = OS_MGMT_HANDLERS; + details_response[FOUND_INDEX_OS].expected_test = false; +#endif + + strcpy(details_response[FOUND_INDEX_ENUM].expected_name, ENUM_MGMT_NAME); + details_response[FOUND_INDEX_ENUM].expected_handlers = ENUM_MGMT_HANDLERS; + details_response[FOUND_INDEX_ENUM].expected_test = false; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("groups", parse_details_entries, &details_response), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 3, buffer, ARRAY_SIZE(buffer), 0); + + ok = create_enum_mgmt_details_packet(zse, buffer, buffer_out, &buffer_size, NULL, 0); + zassert_true(ok, "Expected packet creation to be successful"); + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_ENUM), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, ENUM_MGMT_ID_DETAILS, "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 7, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element"); + + i = 0; + + while (i < TEST_GROUPS) { + zassert_true(details_response[i].matched_name, + "Expected group name to be found in details"); + zassert_true(details_response[i].matched_handlers, + "Expected group handler to be found in details"); + zassert_true(details_response[i].matched_test, + "Did not expect group test to be found in details"); + ++i; + } + + zassert_true(enum_valid_got, "Expected callback to have ran"); + zassert_false(enum_field_added, "Did not expect field to be added"); + zassert_false(event_invalid_got, "Did not expect invalid callback to have ran"); + + /* Clean up test */ + cleanup_test(NULL); +} + +ZTEST(enum_mgmt, test_details_blocked) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + size_t decoded = 0; + uint32_t rc = 0; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_uint32_decode, &rc), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 3, buffer, ARRAY_SIZE(buffer), 0); + + ok = create_enum_mgmt_details_packet(zse, buffer, buffer_out, &buffer_size, NULL, 0); + zassert_true(ok, "Expected packet creation to be successful"); + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Force notification callback to return an error */ + block_access = true; + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_ENUM), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, ENUM_MGMT_ID_DETAILS, "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 5, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element"); + + zassert_true(enum_valid_got, "Expected callback to have ran"); + zassert_false(enum_field_added, "Did not expect field to be added"); + zassert_false(event_invalid_got, "Did not expect invalid callback to have ran"); + + /* Clean up test */ + cleanup_test(NULL); +} + +ZTEST(enum_mgmt, test_details_extra) +{ + uint8_t buffer[ZCBOR_BUFFER_SIZE]; + uint8_t buffer_out[OUTPUT_BUFFER_SIZE]; + bool ok; + uint16_t buffer_size; + zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 }; + bool received; + struct smp_hdr *header; + size_t decoded = 0; + struct details_entries details_response[TEST_GROUPS] = { 0 }; + uint8_t i; + +#if defined(CONFIG_MCUMGR_GRP_SHELL) + strcpy(details_response[FOUND_INDEX_SHELL].expected_name, SHELL_MGMT_NAME); + details_response[FOUND_INDEX_SHELL].expected_handlers = SHELL_MGMT_HANDLERS; + details_response[FOUND_INDEX_SHELL].expected_test = true; +#endif + +#if defined(CONFIG_MCUMGR_GRP_OS) + strcpy(details_response[FOUND_INDEX_OS].expected_name, OS_MGMT_NAME); + details_response[FOUND_INDEX_OS].expected_handlers = OS_MGMT_HANDLERS; + details_response[FOUND_INDEX_OS].expected_test = true; +#endif + + strcpy(details_response[FOUND_INDEX_ENUM].expected_name, ENUM_MGMT_NAME); + details_response[FOUND_INDEX_ENUM].expected_handlers = ENUM_MGMT_HANDLERS; + details_response[FOUND_INDEX_ENUM].expected_test = true; + + struct zcbor_map_decode_key_val output_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("groups", parse_details_entries, &details_response), + }; + + memset(buffer, 0, sizeof(buffer)); + memset(buffer_out, 0, sizeof(buffer_out)); + buffer_size = 0; + memset(zse, 0, sizeof(zse)); + memset(zsd, 0, sizeof(zsd)); + + zcbor_new_encode_state(zse, 3, buffer, ARRAY_SIZE(buffer), 0); + + ok = create_enum_mgmt_details_packet(zse, buffer, buffer_out, &buffer_size, NULL, 0); + zassert_true(ok, "Expected packet creation to be successful"); + + add_field = true; + + /* Enable dummy SMP backend and ready for usage */ + smp_dummy_enable(); + smp_dummy_clear_state(); + + /* Send query command to dummy SMP backend */ + (void)smp_dummy_tx_pkt(buffer_out, buffer_size); + smp_dummy_add_data(); + + /* Wait for a short duration to see if response has been received */ + received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME); + zassert_true(received, "Expected to receive data but timed out"); + + /* Retrieve response buffer */ + nb = smp_dummy_get_outgoing(); + smp_dummy_disable(); + + /* Check response is as expected */ + header = net_buf_pull_mem(nb, sizeof(struct smp_hdr)); + + zassert_equal(header->nh_flags, 0, "SMP header flags mismatch"); + zassert_equal(header->nh_op, MGMT_OP_READ_RSP, "SMP header operation mismatch"); + zassert_equal(header->nh_group, sys_cpu_to_be16(MGMT_GROUP_ID_ENUM), + "SMP header group mismatch"); + zassert_equal(header->nh_seq, 1, "SMP header sequence number mismatch"); + zassert_equal(header->nh_id, ENUM_MGMT_ID_DETAILS, "SMP header command ID mismatch"); + zassert_equal(header->nh_version, 1, "SMP header version mismatch"); + + /* Get the response value to compare */ + zcbor_new_decode_state(zsd, 5, nb->data, nb->len, 1, NULL, 0); + ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0; + zassert_true(ok, "Expected decode to be successful"); + zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element"); + + i = 0; + + while (i < TEST_GROUPS) { + zassert_true(details_response[i].matched_name, + "Expected group name to be found in details"); + zassert_true(details_response[i].matched_handlers, + "Expected group handler to be found in details"); + zassert_true(details_response[i].matched_test, + "Expected group test to be found in details"); + ++i; + } + + zassert_true(enum_valid_got, "Expected callback to have ran"); + zassert_true(enum_field_added, "Expected field to be added"); + zassert_false(event_invalid_got, "Did not expect invalid callback to have ran"); + + /* Clean up test */ + cleanup_test(NULL); +} + +static enum mgmt_cb_return mgmt_event_cmd_callback(uint32_t event, enum mgmt_cb_return prev_status, + int32_t *rc, uint16_t *group, bool *abort_more, + void *data, size_t data_size) +{ + if (event == MGMT_EVT_OP_ENUM_MGMT_DETAILS) { + struct enum_mgmt_detail_output *enum_data = (struct enum_mgmt_detail_output *)data; + + enum_valid_got = true; + + if (add_field == true) { + uint32_t temp = enum_data->group->mg_group_id * 3 + 1; + bool ok; + + ok = zcbor_tstr_put_lit(enum_data->zse, "test") && + zcbor_uint32_encode(enum_data->zse, &temp); + + if (!ok) { + *rc = MGMT_ERR_EUNKNOWN; + return MGMT_CB_ERROR_RC; + } + + enum_field_added = true; + } + + if (block_access == true) { + *rc = MGMT_ERR_EPERUSER; + return MGMT_CB_ERROR_RC; + } + } else { + event_invalid_got = true; + } + + return MGMT_CB_OK; +} + +static struct mgmt_callback mgmt_event_callback = { + .callback = mgmt_event_cmd_callback, + .event_id = MGMT_EVT_OP_ENUM_MGMT_DETAILS, +}; + +static void *setup_test(void) +{ + mgmt_callback_register(&mgmt_event_callback); + + return NULL; +} + +ZTEST_SUITE(enum_mgmt, NULL, setup_test, NULL, cleanup_test, NULL); diff --git a/tests/subsys/mgmt/mcumgr/enum_mgmt/src/smp_test_util.c b/tests/subsys/mgmt/mcumgr/enum_mgmt/src/smp_test_util.c new file mode 100644 index 00000000000..18e5847cb7e --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/enum_mgmt/src/smp_test_util.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022-2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "smp_test_util.h" +#include +#include +#include +#include + +/* SMP header function for generating MCUmgr command header with sequence number set to 1 */ +static void smp_make_hdr(struct smp_hdr *rsp_hdr, size_t len, uint8_t type, bool write) +{ + *rsp_hdr = (struct smp_hdr) { + .nh_len = sys_cpu_to_be16(len), + .nh_flags = 0, + .nh_op = (write ? MGMT_OP_WRITE : MGMT_OP_READ), + .nh_group = sys_cpu_to_be16(MGMT_GROUP_ID_ENUM), + .nh_seq = 1, + .nh_id = type, + .nh_version = 1, + }; +} + +bool create_enum_mgmt_count_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size) +{ + bool ok; + + ok = zcbor_map_start_encode(zse, 2) && + zcbor_map_end_encode(zse, 2); + + if (!ok) { + return false; + } + + *buffer_size = (zse->payload_mut - buffer); + smp_make_hdr((struct smp_hdr *)output_buffer, *buffer_size, ENUM_MGMT_ID_COUNT, false); + memcpy(&output_buffer[sizeof(struct smp_hdr)], buffer, *buffer_size); + *buffer_size += sizeof(struct smp_hdr); + + return true; +} + +bool create_enum_mgmt_list_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size) +{ + bool ok; + + ok = zcbor_map_start_encode(zse, 2) && + zcbor_map_end_encode(zse, 2); + + if (!ok) { + return false; + } + + *buffer_size = (zse->payload_mut - buffer); + smp_make_hdr((struct smp_hdr *)output_buffer, *buffer_size, ENUM_MGMT_ID_LIST, false); + memcpy(&output_buffer[sizeof(struct smp_hdr)], buffer, *buffer_size); + *buffer_size += sizeof(struct smp_hdr); + + return true; +} + +bool create_enum_mgmt_single_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size, uint32_t index) +{ + bool ok; + + ok = zcbor_map_start_encode(zse, 2) && + zcbor_tstr_put_lit(zse, "index") && + zcbor_uint32_put(zse, index) && + zcbor_map_end_encode(zse, 2); + + if (!ok) { + return false; + } + + *buffer_size = (zse->payload_mut - buffer); + smp_make_hdr((struct smp_hdr *)output_buffer, *buffer_size, ENUM_MGMT_ID_SINGLE, false); + memcpy(&output_buffer[sizeof(struct smp_hdr)], buffer, *buffer_size); + *buffer_size += sizeof(struct smp_hdr); + + return true; +} + +bool create_enum_mgmt_details_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size, uint16_t *groups, uint8_t groups_size) +{ + bool ok; + + ok = zcbor_map_start_encode(zse, 2); + + if (groups_size > 0) { + uint8_t i = 0; + + ok &= zcbor_tstr_put_lit(zse, "groups") && + zcbor_list_start_encode(zse, groups_size); + + while (i < groups_size) { + uint32_t group = (uint32_t)groups[i]; + + ok &= zcbor_uint32_encode(zse, &group); + } + + ok &= zcbor_list_end_encode(zse, groups_size); + } + + ok &= zcbor_map_end_encode(zse, 2); + + if (!ok) { + return false; + } + + *buffer_size = (zse->payload_mut - buffer); + smp_make_hdr((struct smp_hdr *)output_buffer, *buffer_size, ENUM_MGMT_ID_DETAILS, false); + memcpy(&output_buffer[sizeof(struct smp_hdr)], buffer, *buffer_size); + *buffer_size += sizeof(struct smp_hdr); + + return true; +} diff --git a/tests/subsys/mgmt/mcumgr/enum_mgmt/src/smp_test_util.h b/tests/subsys/mgmt/mcumgr/enum_mgmt/src/smp_test_util.h new file mode 100644 index 00000000000..8e357e76a8d --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/enum_mgmt/src/smp_test_util.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023-2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_SMP_TEST_UTIL_ +#define H_SMP_TEST_UTIL_ + +#include +#include +#include +#include + +/* Function for creating an enum_mgmt count command */ +bool create_enum_mgmt_count_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size); + +/* Function for creating an enum_mgmt list command */ +bool create_enum_mgmt_list_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size); + +/* Function for creating an enum_mgmt single command */ +bool create_enum_mgmt_single_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size, uint32_t index); + +/* Function for creating an enum_mgmt details command */ +bool create_enum_mgmt_details_packet(zcbor_state_t *zse, uint8_t *buffer, uint8_t *output_buffer, + uint16_t *buffer_size, uint16_t *groups, uint8_t groups_size); + +#endif diff --git a/tests/subsys/mgmt/mcumgr/enum_mgmt/testcase.yaml b/tests/subsys/mgmt/mcumgr/enum_mgmt/testcase.yaml new file mode 100644 index 00000000000..ebfd3014e81 --- /dev/null +++ b/tests/subsys/mgmt/mcumgr/enum_mgmt/testcase.yaml @@ -0,0 +1,31 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +common: + platform_allow: + - native_sim + - native_sim/native/64 + - nrf52840dk/nrf52840 + tags: + - mgmt + - mcumgr + - enum_mgmt +tests: + enum.mgmt: + build_only: false + enum.mgmt.shell: + extra_configs: + - CONFIG_SHELL=y + - CONFIG_SHELL_BACKEND_DUMMY=y + - CONFIG_MCUMGR_GRP_SHELL=y + enum.mgmt.os: + extra_configs: + - CONFIG_MCUMGR_GRP_OS=y + enum.mgmt.shell.os: + extra_configs: + - CONFIG_SHELL=y + - CONFIG_SHELL_BACKEND_DUMMY=y + - CONFIG_MCUMGR_GRP_SHELL=y + - CONFIG_MCUMGR_GRP_OS=y