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 <pascal.brogle@husqvarnagroup.com>
This commit is contained in:
Pascal Brogle 2022-12-08 15:54:50 +01:00 committed by Carles Cufí
commit 6ac281887c
6 changed files with 639 additions and 36 deletions

View file

@ -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
* 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.
*/
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;
}
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) {

View file

@ -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 */

View file

@ -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})

View file

@ -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

View file

@ -0,0 +1,532 @@
/*
* Copyright (c) 2022 GARDENA GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "lwm2m_engine.h"
#include "lwm2m_util.h"
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#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);

View file

@ -0,0 +1,6 @@
common:
depends_on: netif
tests:
net.lwm2m.engine:
platform_allow: native_posix
tags: lwm2m net