From 6ac281887c8fcc6d16d56833f07b582c9a81bffb Mon Sep 17 00:00:00 2001 From: Pascal Brogle Date: Thu, 8 Dec 2022 15:54:50 +0100 Subject: [PATCH] net: lwm2m: fix observation path list ordering under certain conditions the current implementation did not maintain the desired sort order. Signed-off-by: Pascal Brogle --- subsys/net/lib/lwm2m/lwm2m_observation.c | 94 ++-- subsys/net/lib/lwm2m/lwm2m_observation.h | 20 +- tests/net/lib/lwm2m/engine/CMakeLists.txt | 12 + tests/net/lib/lwm2m/engine/prj.conf | 11 + .../lib/lwm2m/engine/src/lwm2m_observation.c | 532 ++++++++++++++++++ tests/net/lib/lwm2m/engine/testcase.yaml | 6 + 6 files changed, 639 insertions(+), 36 deletions(-) create mode 100644 tests/net/lib/lwm2m/engine/CMakeLists.txt create mode 100644 tests/net/lib/lwm2m/engine/prj.conf create mode 100644 tests/net/lib/lwm2m/engine/src/lwm2m_observation.c create mode 100644 tests/net/lib/lwm2m/engine/testcase.yaml diff --git a/subsys/net/lib/lwm2m/lwm2m_observation.c b/subsys/net/lib/lwm2m/lwm2m_observation.c index c20f9b5fce4..5ff4f0d1132 100644 --- a/subsys/net/lib/lwm2m/lwm2m_observation.c +++ b/subsys/net/lib/lwm2m/lwm2m_observation.c @@ -1499,7 +1499,9 @@ int lwm2m_engine_add_path_to_list(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm new_entry->path = *path; if (!sys_slist_is_empty(lwm2m_path_list)) { - /* Keep list Ordered by Object ID/ Object instance/ resource ID */ + /* Keep list Ordered by Object ID / Object instance / resource ID / + * Resource Instance ID + */ SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) { if (entry->path.level == LWM2M_PATH_LEVEL_NONE || lwm2m_obj_path_equal(&entry->path, &new_entry->path)) { @@ -1508,39 +1510,63 @@ int lwm2m_engine_add_path_to_list(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm return 0; } - if (entry->path.obj_id > path->obj_id) { - /* New entry have smaller Object ID */ - add_before_current = true; - } else if (entry->path.obj_id == path->obj_id && - entry->path.level > path->level) { - add_before_current = true; - } else if (entry->path.obj_id == path->obj_id && - entry->path.level == path->level) { - if (path->level >= LWM2M_PATH_LEVEL_OBJECT_INST && - entry->path.obj_inst_id > path->obj_inst_id) { - /* - * New have same Object ID - * but smaller Object Instance ID - */ - add_before_current = true; - } else if (path->level >= LWM2M_PATH_LEVEL_RESOURCE && - entry->path.obj_inst_id == path->obj_inst_id && - entry->path.res_id > path->res_id) { - /* - * Object ID and Object Instance id same - * but Resource ID is smaller - */ - add_before_current = true; - } else if (path->level >= LWM2M_PATH_LEVEL_RESOURCE_INST && - entry->path.obj_inst_id == path->obj_inst_id && - entry->path.res_id == path->res_id && - entry->path.res_inst_id > path->res_inst_id) { - /* - * Object ID, Object Instance id & Resource ID same - * but Resource instance ID is smaller - */ - add_before_current = true; - } + /* + * algorithm assumes that list is already properly sorted and that + * there are no duplicates. general idea: + * - if at any level up to new entry's path level, IDs below the level + * match and the ID of the new entry at that level is smaller, + * insert before. + * - if all IDs of the new entry match the existing entry, insert before. + * Because of sorting and no duplicates, the existing entry must have + * higher path level and come after the new entry. + */ + switch (new_entry->path.level) { + case LWM2M_PATH_LEVEL_OBJECT: + add_before_current = new_entry->path.obj_id <= entry->path.obj_id; + break; + + case LWM2M_PATH_LEVEL_OBJECT_INST: + add_before_current = + (new_entry->path.obj_id < entry->path.obj_id) || + + (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST && + entry->path.obj_id == new_entry->path.obj_id && + new_entry->path.obj_inst_id <= entry->path.obj_inst_id); + break; + + case LWM2M_PATH_LEVEL_RESOURCE: + add_before_current = + (new_entry->path.obj_id < entry->path.obj_id) || + + (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST && + entry->path.obj_id == new_entry->path.obj_id && + new_entry->path.obj_inst_id < entry->path.obj_inst_id) || + + (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE && + entry->path.obj_id == new_entry->path.obj_id && + entry->path.obj_inst_id == new_entry->path.obj_inst_id && + new_entry->path.res_id <= entry->path.res_id); + break; + + case LWM2M_PATH_LEVEL_RESOURCE_INST: + add_before_current = + (new_entry->path.obj_id < entry->path.obj_id) || + + (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST && + entry->path.obj_id == new_entry->path.obj_id && + new_entry->path.obj_inst_id < entry->path.obj_inst_id) || + + (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE && + entry->path.obj_id == new_entry->path.obj_id && + entry->path.obj_inst_id == new_entry->path.obj_inst_id && + new_entry->path.res_id < entry->path.res_id) || + + (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE_INST && + entry->path.obj_id == new_entry->path.obj_id && + entry->path.obj_inst_id == new_entry->path.obj_inst_id && + entry->path.res_id == new_entry->path.res_id && + new_entry->path.res_inst_id <= entry->path.res_inst_id); + break; } if (add_before_current) { diff --git a/subsys/net/lib/lwm2m/lwm2m_observation.h b/subsys/net/lib/lwm2m/lwm2m_observation.h index 3f0794ddcfb..e48b9a06698 100644 --- a/subsys/net/lib/lwm2m/lwm2m_observation.h +++ b/subsys/net/lib/lwm2m/lwm2m_observation.h @@ -61,13 +61,29 @@ struct lwm2m_obj_path_list { void lwm2m_engine_path_list_init(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list, struct lwm2m_obj_path_list path_object_buf[], uint8_t path_object_size); -/* Add new Path to the list */ +/** + * Add new path to the list while maintaining hierarchical sort order + * + * @param lwm2m_path_list sorted path list + * @param lwm2m_free_list free list + * @param path path to be added + * @return 0 on success or a negative error code + */ int lwm2m_engine_add_path_to_list(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list, struct lwm2m_obj_path *path); int lwm2m_get_path_reference_ptr(struct lwm2m_engine_obj *obj, struct lwm2m_obj_path *path, void **ref); -/* Remove paths when parent already exist in the list. */ + +/** + * Remove paths when parent already exists in the list + * + * @note Path list must be sorted + * @see lwm2m_engine_add_path_to_list() + * + * @param lwm2m_path_list sorted path list + * @param lwm2m_free_list free list + */ void lwm2m_engine_clear_duplicate_path(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list); /* Resources */ diff --git a/tests/net/lib/lwm2m/engine/CMakeLists.txt b/tests/net/lib/lwm2m/engine/CMakeLists.txt new file mode 100644 index 00000000000..1f9de3ad3f7 --- /dev/null +++ b/tests/net/lib/lwm2m/engine/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(lwm2m_engine) + +target_include_directories(app PRIVATE + ${ZEPHYR_BASE}/subsys/net/lib/lwm2m + ) +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/net/lib/lwm2m/engine/prj.conf b/tests/net/lib/lwm2m/engine/prj.conf new file mode 100644 index 00000000000..18a5df2ad22 --- /dev/null +++ b/tests/net/lib/lwm2m/engine/prj.conf @@ -0,0 +1,11 @@ +CONFIG_NETWORKING=y +CONFIG_NET_TEST=y +CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y + +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NEWLIB_LIBC=y + +CONFIG_LWM2M=y +CONFIG_LWM2M_COAP_MAX_MSG_SIZE=512 diff --git a/tests/net/lib/lwm2m/engine/src/lwm2m_observation.c b/tests/net/lib/lwm2m/engine/src/lwm2m_observation.c new file mode 100644 index 00000000000..185f7fe19de --- /dev/null +++ b/tests/net/lib/lwm2m/engine/src/lwm2m_observation.c @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2022 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "lwm2m_engine.h" +#include "lwm2m_util.h" + +#include +#include + +#define TEST_VERBOSE 0 + +#if TEST_VERBOSE +#define TEST_VERBOSE_PRINT(fmt, ...) TC_PRINT(fmt, ##__VA_ARGS__) +#else +#define TEST_VERBOSE_PRINT(fmt, ...) +#endif + +static bool lwm2m_path_object_equal_upto(struct lwm2m_obj_path *path, + struct lwm2m_obj_path *compare_path, uint8_t level) +{ + + if (level >= LWM2M_PATH_LEVEL_OBJECT && path->obj_id != compare_path->obj_id) { + return false; + } + + if (level >= LWM2M_PATH_LEVEL_OBJECT_INST && + path->obj_inst_id != compare_path->obj_inst_id) { + return false; + } + + if (level >= LWM2M_PATH_LEVEL_RESOURCE && path->res_id != compare_path->res_id) { + return false; + } + + if (level >= LWM2M_PATH_LEVEL_RESOURCE_INST && + path->res_inst_id != compare_path->res_inst_id) { + return false; + } + + return true; +} + +static void assert_path_list_order(sys_slist_t *lwm2m_path_list) +{ + + struct lwm2m_obj_path_list *prev = NULL; + struct lwm2m_obj_path_list *entry, *tmp; + + uint16_t obj_id_max; + uint16_t obj_inst_id_max; + uint16_t res_id_max; + uint16_t res_inst_id_max; + + char next_path_str[LWM2M_MAX_PATH_STR_LEN]; + char prev_path_str[LWM2M_MAX_PATH_STR_LEN]; + + if (sys_slist_is_empty(lwm2m_path_list)) { + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(lwm2m_path_list, entry, tmp, node) { + if (prev) { + if (entry->path.level > prev->path.level) { + + lwm2m_path_to_string(next_path_str, sizeof(next_path_str), + &entry->path, entry->path.level); + lwm2m_path_to_string(prev_path_str, sizeof(prev_path_str), + &prev->path, prev->path.level); + + bool is_after = false; + + if (prev->path.level >= LWM2M_PATH_LEVEL_OBJECT) { + is_after = entry->path.obj_id >= prev->path.obj_id; + } + if (is_after && prev->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST && + entry->path.obj_id == prev->path.obj_id) { + is_after = + entry->path.obj_inst_id >= prev->path.obj_inst_id; + } + + if (is_after && prev->path.level >= LWM2M_PATH_LEVEL_RESOURCE && + entry->path.obj_inst_id == prev->path.obj_inst_id) { + is_after = entry->path.res_id >= prev->path.res_id; + } + + if (is_after && + prev->path.level >= LWM2M_PATH_LEVEL_RESOURCE_INST && + entry->path.res_id == prev->path.res_id) { + is_after = + entry->path.res_inst_id >= prev->path.res_inst_id; + } + + zassert_true(is_after, "Next element %s must be before previous %s", + next_path_str, prev_path_str); + } else if (entry->path.level == prev->path.level) { + + if (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT) { + zassert_true(entry->path.obj_id >= obj_id_max, + "Next element has object %d which is smaller " + "than previous max object %d", + entry->path.obj_id, obj_id_max); + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) { + if (!lwm2m_path_object_equal_upto( + &entry->path, &prev->path, + LWM2M_PATH_LEVEL_OBJECT)) { + /* reset max id when path changed */ + obj_inst_id_max = 0; + } + + zassert_true(entry->path.obj_inst_id >= obj_inst_id_max, + "Next element has object instance %d which is " + "smaller " + "than previous max object instance %d", + entry->path.obj_inst_id, obj_inst_id_max); + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE) { + if (!lwm2m_path_object_equal_upto( + &entry->path, &prev->path, + LWM2M_PATH_LEVEL_OBJECT_INST)) { + /* reset max id when path changed */ + res_id_max = 0; + } + + zassert_true( + entry->path.res_id >= res_id_max, + "Next element has resource %d which is smaller " + "than previous max resource %d", + entry->path.res_id, res_id_max); + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE_INST) { + if (!lwm2m_path_object_equal_upto( + &entry->path, &prev->path, + LWM2M_PATH_LEVEL_RESOURCE)) { + /* reset max id when path changed */ + res_inst_id_max = 0; + } + zassert_true(entry->path.res_inst_id >= res_inst_id_max, + "Next element has resource instance %d which " + "is smaller " + "than previous max resource instance %d", + entry->path.res_inst_id, res_inst_id_max); + } + + } else { /* entry->path.level < prev->path.level */ + if (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT) { + zassert_true(entry->path.obj_id >= obj_id_max, + "Next element has object %d which is smaller " + "than previous max object %d", + entry->path.obj_id, obj_id_max); + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) { + zassert_true(entry->path.obj_inst_id >= obj_inst_id_max, + "Next element has object instance %d which is " + "smaller " + "than previous max object instance %d", + entry->path.obj_inst_id, obj_inst_id_max); + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE) { + zassert_true( + entry->path.res_id >= res_id_max, + "Next element has resource %d which is smaller " + "than previous max resource %d", + entry->path.res_id, res_id_max); + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE_INST) { + zassert_true(entry->path.res_inst_id >= res_inst_id_max, + "Next element has resource instance %d which " + "is bigger " + "than previous max resource instance %d", + entry->path.res_inst_id, res_inst_id_max); + } + + zassert_true(!lwm2m_path_object_equal_upto( + &entry->path, &prev->path, entry->path.level), + "Next element equals previous up to level %d " + "and thus must be before previous", + entry->path.level); + } + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT) { + obj_id_max = entry->path.obj_id; + } else { + obj_id_max = 0; + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST && + (prev == NULL || entry->path.obj_id == prev->path.obj_id)) { + obj_inst_id_max = entry->path.obj_inst_id; + } else { + obj_inst_id_max = 0; + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE && + (prev == NULL || entry->path.obj_id == prev->path.obj_id) && + (prev == NULL || entry->path.obj_inst_id == prev->path.obj_inst_id)) { + res_id_max = entry->path.res_id; + } else { + res_id_max = 0; + } + + if (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE_INST && + (prev == NULL || entry->path.obj_id == prev->path.obj_id) && + (prev == NULL || entry->path.obj_inst_id == prev->path.obj_inst_id) && + (prev == NULL || entry->path.res_id == prev->path.res_id)) { + res_inst_id_max = entry->path.res_inst_id; + } else { + res_inst_id_max = 0; + } + + prev = entry; + } +} +static void print_path_list(sys_slist_t *lwm2m_path_list, const char *name) +{ + struct lwm2m_obj_path_list *entry, *tmp; + + if (name != NULL) { + TEST_VERBOSE_PRINT("Path List %s:\n", name); + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(lwm2m_path_list, entry, tmp, node) { + char buf[LWM2M_MAX_PATH_STR_LEN]; + + lwm2m_path_to_string(buf, LWM2M_MAX_PATH_STR_LEN, &entry->path, entry->path.level); + TEST_VERBOSE_PRINT("- %s\n", buf); + } +} + +static void run_insertion_test(char const *insert_path_str[], int insertions_count, + char const *expected_path_str[]) +{ + /* GIVEN: different paths */ + struct lwm2m_obj_path_list lwm2m_path_list_buf[insertions_count]; + sys_slist_t lwm2m_path_list; + sys_slist_t lwm2m_path_free_list; + int ret; + + lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf, + insertions_count); + + /* WHEN: inserting each path */ + struct lwm2m_obj_path insert_path; + char name[20]; + + for (int i = 0; i < insertions_count; ++i) { + ret = lwm2m_string_to_path(insert_path_str[i], &insert_path, '/'); + zassert_true(ret >= 0, "Conversion to path #%d failed", i); + + ret = lwm2m_engine_add_path_to_list(&lwm2m_path_list, &lwm2m_path_free_list, + &insert_path); + + sprintf(name, "Insertion: %d", i); + print_path_list(&lwm2m_path_list, name); + zassert_true(ret >= 0, "Insertion #%d failed", i); + /* THEN: path order is maintained */ + assert_path_list_order(&lwm2m_path_list); + } + + print_path_list(&lwm2m_path_list, "Final"); + + /* AND: final list matches expectation */ + struct lwm2m_obj_path_list *entry, *tmp; + int path_num = 0; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&lwm2m_path_list, entry, tmp, node) { + struct lwm2m_obj_path expected_path; + + lwm2m_string_to_path(expected_path_str[path_num++], &expected_path, '/'); + zassert_mem_equal(&entry->path, &expected_path, sizeof(struct lwm2m_obj_path), + "Path #%d did not match expectation", path_num); + } +} + +ZTEST(lwm2m_observation, test_add_path_to_list) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(2), + LWM2M_PATH(1), + LWM2M_PATH(1, 2), + LWM2M_PATH(1, 1), + LWM2M_PATH(1, 1, 10), + LWM2M_PATH(1, 1, 10, 10), + LWM2M_PATH(1, 1, 10, 9), + LWM2M_PATH(1, 2, 10, 11), + + LWM2M_PATH(100), + + LWM2M_PATH(41), + LWM2M_PATH(43, 3), + LWM2M_PATH(45, 2, 2), + LWM2M_PATH(47, 1, 1, 1), + + LWM2M_PATH(57, 1, 1, 1), + LWM2M_PATH(55, 2, 2), + LWM2M_PATH(53, 3), + LWM2M_PATH(51), + }; + + char const *expected_path_str[] = { + LWM2M_PATH(1), + LWM2M_PATH(1, 1), + LWM2M_PATH(1, 1, 10), + LWM2M_PATH(1, 1, 10, 9), + LWM2M_PATH(1, 1, 10, 10), + LWM2M_PATH(1, 2), + LWM2M_PATH(1, 2, 10, 11), + LWM2M_PATH(2), + LWM2M_PATH(41), + LWM2M_PATH(43, 3), + LWM2M_PATH(45, 2, 2), + LWM2M_PATH(47, 1, 1, 1), + LWM2M_PATH(51), + LWM2M_PATH(53, 3), + LWM2M_PATH(55, 2, 2), + LWM2M_PATH(57, 1, 1, 1), + LWM2M_PATH(100), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_inverse_non_overlapping) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(41), + LWM2M_PATH(43, 3), + LWM2M_PATH(45, 2, 2), + LWM2M_PATH(47, 1, 1, 1), + }; + + char const *expected_path_str[] = { + LWM2M_PATH(41), + LWM2M_PATH(43, 3), + LWM2M_PATH(45, 2, 2), + LWM2M_PATH(47, 1, 1, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_inverse_non_overlapping_2) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(57, 1, 1, 1), + LWM2M_PATH(55, 2, 2), + LWM2M_PATH(53, 3), + LWM2M_PATH(51), + }; + + char const *expected_path_str[] = { + LWM2M_PATH(51), + LWM2M_PATH(53, 3), + LWM2M_PATH(55, 2, 2), + LWM2M_PATH(57, 1, 1, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_object_before_resource_inst) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(1, 1, 1, 1), + LWM2M_PATH(1), + }; + + char const *expected_path_str[6] = { + LWM2M_PATH(1), + LWM2M_PATH(1, 1, 1, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_object_inst_before_resource_inst) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(1, 1, 1, 1), + LWM2M_PATH(1, 1), + }; + + char const *expected_path_str[6] = { + LWM2M_PATH(1, 1), + LWM2M_PATH(1, 1, 1, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_resource_before_resource_inst) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(1, 1, 1, 1), + LWM2M_PATH(1, 1, 1), + }; + + char const *expected_path_str[] = { + LWM2M_PATH(1, 1, 1), + LWM2M_PATH(1, 1, 1, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_resource_order) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(32765, 1, 6, 0), + LWM2M_PATH(32765, 1, 6, 1), + LWM2M_PATH(32765, 1, 6), + LWM2M_PATH(32765, 1, 5), + LWM2M_PATH(32765, 1, 5, 2), + LWM2M_PATH(32765, 1, 5, 1), + }; + + char const *expected_path_str[] = { + LWM2M_PATH(32765, 1, 5), + LWM2M_PATH(32765, 1, 5, 1), + LWM2M_PATH(32765, 1, 5, 2), + LWM2M_PATH(32765, 1, 6), + LWM2M_PATH(32765, 1, 6, 0), + LWM2M_PATH(32765, 1, 6, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_resource_before_instance) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(32765, 1, 6, 0), + LWM2M_PATH(32765, 1, 6, 1), + LWM2M_PATH(32765, 1, 6), + }; + + char const *expected_path_str[6] = { + LWM2M_PATH(32765, 1, 6), + LWM2M_PATH(32765, 1, 6, 0), + LWM2M_PATH(32765, 1, 6, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_resource_inverse) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(1, 1, 1, 1), + LWM2M_PATH(1, 1, 1), + LWM2M_PATH(1, 1), + LWM2M_PATH(1), + }; + + char const *expected_path_str[] = { + LWM2M_PATH(1), + LWM2M_PATH(1, 1), + LWM2M_PATH(1, 1, 1), + LWM2M_PATH(1, 1, 1, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_obj_after_resource) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(1), + LWM2M_PATH(1, 1), + LWM2M_PATH(1, 1, 1), + LWM2M_PATH(1, 2), + }; + + char const *expected_path_str[] = { + LWM2M_PATH(1), + LWM2M_PATH(1, 1), + LWM2M_PATH(1, 1, 1), + LWM2M_PATH(1, 2), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST(lwm2m_observation, test_add_path_to_list_duplicate) +{ + /* clang-format off */ + char const *insert_path_str[] = { + LWM2M_PATH(1), + LWM2M_PATH(1, 1), + LWM2M_PATH(1), + }; + + char const *expected_path_str[] = { + LWM2M_PATH(1), + LWM2M_PATH(1, 1), + }; + /* clang-format on */ + + run_insertion_test(insert_path_str, ARRAY_SIZE(insert_path_str), expected_path_str); +} + +ZTEST_SUITE(lwm2m_observation, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/net/lib/lwm2m/engine/testcase.yaml b/tests/net/lib/lwm2m/engine/testcase.yaml new file mode 100644 index 00000000000..7ce9d7e3907 --- /dev/null +++ b/tests/net/lib/lwm2m/engine/testcase.yaml @@ -0,0 +1,6 @@ +common: + depends_on: netif +tests: + net.lwm2m.engine: + platform_allow: native_posix + tags: lwm2m net