1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/sys/util.h>
9 #include "lwm2m_engine.h"
10 #include "lwm2m_util.h"
11 
12 #define TEST_OBJ_ID 32768
13 
14 static uint32_t callback_checker;
15 static char pre_write_cb_buf[10];
16 
pre_write_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)17 static void *pre_write_cb(uint16_t obj_inst_id,
18 			  uint16_t res_id,
19 			  uint16_t res_inst_id,
20 			  size_t *data_len)
21 {
22 	callback_checker |= 0x01;
23 	return pre_write_cb_buf;
24 }
25 
post_write_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,uint8_t * data,uint16_t data_len,bool last_block,size_t total_size,size_t offset)26 static int post_write_cb(uint16_t obj_inst_id, uint16_t res_id,
27 			 uint16_t res_inst_id, uint8_t *data,
28 			 uint16_t data_len, bool last_block,
29 			 size_t total_size, size_t offset)
30 {
31 	callback_checker |= 0x02;
32 	return 0;
33 }
34 
read_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)35 static void *read_cb(uint16_t obj_inst_id,
36 		     uint16_t res_id,
37 		     uint16_t res_inst_id,
38 		     size_t *data_len)
39 {
40 	callback_checker |= 0x04;
41 	return 0;
42 }
43 
validate_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,uint8_t * data,uint16_t data_len,bool last_block,size_t total_size,size_t offset)44 static int validate_cb(uint16_t obj_inst_id, uint16_t res_id,
45 		       uint16_t res_inst_id, uint8_t *data, uint16_t data_len,
46 		       bool last_block, size_t total_size, size_t offset)
47 {
48 	callback_checker |= 0x08;
49 	return 0;
50 }
51 
obj_create_cb(uint16_t obj_inst_id)52 static int obj_create_cb(uint16_t obj_inst_id)
53 {
54 	callback_checker |= 0x10;
55 	return 0;
56 }
57 
obj_delete_cb(uint16_t obj_inst_id)58 static int obj_delete_cb(uint16_t obj_inst_id)
59 {
60 	callback_checker |= 0x20;
61 	return 0;
62 }
63 
exec_cb(uint16_t obj_inst_id,uint8_t * args,uint16_t args_len)64 static int exec_cb(uint16_t obj_inst_id, uint8_t *args, uint16_t args_len)
65 {
66 	callback_checker |= 0x40;
67 	return 0;
68 }
69 
70 ZTEST_SUITE(lwm2m_registry, NULL, NULL, NULL, NULL, NULL);
71 
ZTEST(lwm2m_registry,test_object_creation_and_deletion)72 ZTEST(lwm2m_registry, test_object_creation_and_deletion)
73 {
74 	int ret;
75 
76 	ret = lwm2m_create_object_inst(&LWM2M_OBJ(3303, 0));
77 	zassert_equal(ret, 0);
78 
79 	ret = lwm2m_delete_object_inst(&LWM2M_OBJ(3303, 0));
80 	zassert_equal(ret, 0);
81 }
82 
ZTEST(lwm2m_registry,test_create_unknown_object)83 ZTEST(lwm2m_registry, test_create_unknown_object)
84 {
85 	int ret;
86 
87 	ret = lwm2m_create_object_inst(&LWM2M_OBJ(49999, 0));
88 	zassert_equal(ret, -ENOENT);
89 }
90 
ZTEST(lwm2m_registry,test_resource_buf)91 ZTEST(lwm2m_registry, test_resource_buf)
92 {
93 	int ret;
94 	uint8_t resource_buf = 0;
95 
96 	ret = lwm2m_create_object_inst(&LWM2M_OBJ(3303, 0));
97 	zassert_equal(ret, 0);
98 
99 	ret = lwm2m_set_res_buf(&LWM2M_OBJ(3303, 0, 6042), &resource_buf, sizeof(resource_buf),
100 				sizeof(resource_buf), 0);
101 	zassert_equal(ret, 0);
102 
103 	ret = lwm2m_set_u8(&LWM2M_OBJ(3303, 0, 6042), 0x5A);
104 	zassert_equal(ret, 0);
105 
106 	zassert_equal(resource_buf, 0x5A);
107 
108 	ret = lwm2m_delete_object_inst(&LWM2M_OBJ(3303, 0));
109 	zassert_equal(ret, 0);
110 }
111 
ZTEST(lwm2m_registry,test_unknown_res)112 ZTEST(lwm2m_registry, test_unknown_res)
113 {
114 	int ret;
115 	uint8_t resource_buf = 0;
116 
117 	ret = lwm2m_create_object_inst(&LWM2M_OBJ(3303, 0));
118 	zassert_equal(ret, 0);
119 
120 	ret = lwm2m_set_res_buf(&LWM2M_OBJ(3303, 0, 49999), &resource_buf, sizeof(resource_buf),
121 				sizeof(resource_buf), 0);
122 	zassert_equal(ret, -ENOENT);
123 
124 	ret = lwm2m_delete_object_inst(&LWM2M_OBJ(3303, 0));
125 	zassert_equal(ret, 0);
126 }
127 
ZTEST(lwm2m_registry,test_get_res_inst)128 ZTEST(lwm2m_registry, test_get_res_inst)
129 {
130 	zassert_is_null(lwm2m_engine_get_res_inst(&LWM2M_OBJ(3)));
131 	zassert_is_null(lwm2m_engine_get_res_inst(&LWM2M_OBJ(3, 0)));
132 	zassert_is_null(lwm2m_engine_get_res_inst(&LWM2M_OBJ(3, 0, 11)));
133 	zassert_not_null(lwm2m_engine_get_res_inst(&LWM2M_OBJ(3, 0, 11, 0)));
134 }
135 
136 #define GET_SET_UNINIT_PATTERN 0xA5
137 #define GET_SET_INT(c_type, name, init)                                                            \
138 	struct {                                                                                   \
139 		IDENTITY c_type out;                                                               \
140 		IDENTITY c_type in;                                                                \
141 	} name = {.out = init};                                                                    \
142 	memset(&name.in, GET_SET_UNINIT_PATTERN, sizeof(name.in));                                 \
143 	zassert_not_ok(memcmp(&name.out, &name.in, sizeof(name.out)), "init value must be "        \
144 								      "non-zero")
145 
146 #define GET_SET_ARRAY(c_type, name, init)                                                          \
147 	struct {                                                                                   \
148 		IDENTITY c_type out[sizeof((IDENTITY c_type[])__DEBRACKET init)];                  \
149 		IDENTITY c_type in[sizeof((IDENTITY c_type[])__DEBRACKET init)];                   \
150 	} name = {.out = __DEBRACKET init};                                                        \
151 	memset(&name.in, GET_SET_UNINIT_PATTERN, sizeof(name.in));                                 \
152 	zassert_not_ok(memcmp(&name.out, &name.in, sizeof(name.out)), "init value must be "        \
153 								      "non-zero")
154 
155 #define GET_SET_STRING(c_type, name, init)                                                         \
156 	struct {                                                                                   \
157 		IDENTITY c_type out[sizeof(init)];                                                 \
158 		IDENTITY c_type in[sizeof(init)];                                                  \
159 	} name = {.out = init};                                                                    \
160 	memset(&name.in, GET_SET_UNINIT_PATTERN, sizeof(name.in));                                 \
161 	zassert_not_ok(memcmp(&name.out, &name.in, sizeof(name.out)), "init value must be "        \
162 								      "non-zero")
163 
164 #define GET_SET_STRUCT(c_type, name, init)                                                         \
165 	struct {                                                                                   \
166 		IDENTITY c_type out;                                                               \
167 		IDENTITY c_type in;                                                                \
168 	} name = {.out = __DEBRACKET init};                                                        \
169 	memset(&name.in, GET_SET_UNINIT_PATTERN, sizeof(name.in));                                 \
170 	zassert_not_ok(memcmp(&name.out, &name.in, sizeof(name.out)), "init value must be "        \
171 								      "non-zero")
172 
ZTEST(lwm2m_registry,test_get_set)173 ZTEST(lwm2m_registry, test_get_set)
174 {
175 	GET_SET_INT((bool), b, true);
176 	GET_SET_ARRAY((uint8_t), opaque, ({0xde, 0xad, 0xbe, 0xff, 0, 0}));
177 	GET_SET_STRING((char), string, "Hello");
178 	GET_SET_INT((uint8_t), u8, 8);
179 	GET_SET_INT((int8_t), s8, -8);
180 	GET_SET_INT((uint16_t), u16, 16);
181 	GET_SET_INT((int16_t), s16, -16);
182 	GET_SET_INT((uint32_t), u32, 32);
183 	GET_SET_INT((int32_t), s32, -32);
184 	GET_SET_INT((int64_t), s64, -64);
185 	GET_SET_INT((time_t), t, 1687949519);
186 	GET_SET_INT((double), d, 3.1415);
187 	GET_SET_STRUCT((struct lwm2m_objlnk), objl, ({.obj_id = 1, .obj_inst = 2}));
188 
189 	/* set all resources */
190 	zassert_ok(lwm2m_set_bool(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_BOOL), b.out));
191 	zassert_ok(lwm2m_set_opaque(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_OPAQUE), opaque.out,
192 				    sizeof(opaque.out)));
193 	zassert_ok(lwm2m_set_string(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_STRING), string.out));
194 	zassert_ok(lwm2m_set_u8(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U8), u8.out));
195 	zassert_ok(lwm2m_set_s8(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S8), s8.out));
196 	zassert_ok(lwm2m_set_u16(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U16), u16.out));
197 	zassert_ok(lwm2m_set_s16(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S16), s16.out));
198 	zassert_ok(lwm2m_set_u32(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U32), u32.out));
199 	zassert_ok(lwm2m_set_s32(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S32), s32.out));
200 	zassert_ok(lwm2m_set_s64(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S64), s64.out));
201 	zassert_ok(lwm2m_set_time(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_TIME), t.out));
202 	zassert_ok(lwm2m_set_f64(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_FLOAT), d.out));
203 	zassert_ok(lwm2m_set_objlnk(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_OBJLNK), &objl.out));
204 
205 	/* get all resources */
206 	zassert_ok(lwm2m_get_bool(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_BOOL), &b.in));
207 	zassert_ok(lwm2m_get_opaque(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_OPAQUE), opaque.in,
208 				    sizeof(opaque.in)));
209 	zassert_ok(lwm2m_get_string(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_STRING), string.in,
210 				    sizeof(string.in)));
211 	zassert_ok(lwm2m_get_u8(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U8), &u8.in));
212 	zassert_ok(lwm2m_get_s8(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S8), &s8.in));
213 	zassert_ok(lwm2m_get_u16(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U16), &u16.in));
214 	zassert_ok(lwm2m_get_s16(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S16), &s16.in));
215 	zassert_ok(lwm2m_get_u32(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U32), &u32.in));
216 	zassert_ok(lwm2m_get_s32(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S32), &s32.in));
217 	zassert_ok(lwm2m_get_s64(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S64), &s64.in));
218 	zassert_ok(lwm2m_get_time(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_TIME), &t.in));
219 	zassert_ok(lwm2m_get_f64(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_FLOAT), &d.in));
220 	zassert_ok(lwm2m_get_objlnk(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_OBJLNK), &objl.in));
221 
222 	/* check read value */
223 	zassert_equal(b.in, b.out);
224 	zassert_mem_equal(opaque.in, opaque.out, sizeof(opaque.out));
225 	zassert_str_equal(string.in, string.out);
226 	zassert_equal(u8.in, u8.out);
227 	zassert_equal(s8.in, s8.out);
228 	zassert_equal(u16.in, u16.out);
229 	zassert_equal(s16.in, s16.out);
230 	zassert_equal(u32.in, u32.out);
231 	zassert_equal(s32.in, s32.out);
232 	zassert_equal(s64.in, s64.out);
233 	zassert_equal(t.in, t.out);
234 	zassert_equal(d.in, d.out);
235 	zassert_mem_equal(&objl.in, &objl.out, sizeof(objl.out));
236 
237 	/* set string resource to zero length */
238 	zassert_ok(lwm2m_set_res_data_len(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_STRING), 0));
239 	char buf[10];
240 	zassert_ok(lwm2m_get_string(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_STRING), buf, sizeof(buf)));
241 	zassert_equal(strlen(buf), 0);
242 }
243 
ZTEST(lwm2m_registry,test_temp_sensor)244 ZTEST(lwm2m_registry, test_temp_sensor)
245 {
246 	int ret;
247 	uint8_t u8_buf = 0;
248 	time_t time_buf = 0;
249 	double dbl_buf = 0;
250 	char char_buf[10];
251 
252 	uint8_t u8_getbuf = 0;
253 	time_t time_getbuf = 0;
254 	double dbl_getbuf = 0;
255 	char char_getbuf[10];
256 
257 	ret = lwm2m_create_object_inst(&LWM2M_OBJ(3303, 0));
258 	zassert_equal(ret, 0);
259 
260 	ret = lwm2m_set_res_buf(&LWM2M_OBJ(3303, 0, 6042), &u8_buf, sizeof(u8_buf),
261 				sizeof(u8_buf), 0);
262 	zassert_equal(ret, 0);
263 	ret = lwm2m_set_res_buf(&LWM2M_OBJ(3303, 0, 5518), &time_buf, sizeof(time_buf),
264 				sizeof(time_buf), 0);
265 	zassert_equal(ret, 0);
266 	ret = lwm2m_set_res_buf(&LWM2M_OBJ(3303, 0, 5601), &dbl_buf, sizeof(dbl_buf),
267 				sizeof(dbl_buf), 0);
268 	zassert_equal(ret, 0);
269 	ret = lwm2m_set_res_buf(&LWM2M_OBJ(3303, 0, 5701), &char_buf, sizeof(char_buf),
270 				sizeof(char_buf), 0);
271 	zassert_equal(ret, 0);
272 
273 	ret = lwm2m_set_u8(&LWM2M_OBJ(3303, 0, 6042), 0x5A);
274 	zassert_equal(ret, 0);
275 	ret = lwm2m_set_time(&LWM2M_OBJ(3303, 0, 5518), 1674118825);
276 	zassert_equal(ret, 0);
277 	ret = lwm2m_set_f64(&LWM2M_OBJ(3303, 0, 5601), 5.89);
278 	zassert_equal(ret, 0);
279 	ret = lwm2m_set_string(&LWM2M_OBJ(3303, 0, 5701), "test");
280 	zassert_equal(ret, 0);
281 
282 	zassert_equal(u8_buf, 0x5A);
283 	zassert_equal(time_buf, 1674118825);
284 	zassert_within(dbl_buf, 5.89, 0.01);
285 	zassert_equal(strncmp(char_buf, "test", 10), 0);
286 
287 	ret = lwm2m_get_u8(&LWM2M_OBJ(3303, 0, 6042), &u8_getbuf);
288 	zassert_equal(ret, 0);
289 	ret = lwm2m_get_time(&LWM2M_OBJ(3303, 0, 5518), &time_getbuf);
290 	zassert_equal(ret, 0);
291 	ret = lwm2m_get_f64(&LWM2M_OBJ(3303, 0, 5601), &dbl_getbuf);
292 	zassert_equal(ret, 0);
293 	ret = lwm2m_get_string(&LWM2M_OBJ(3303, 0, 5701), &char_getbuf, 10);
294 	zassert_equal(ret, 0);
295 
296 	zassert_equal(u8_buf, u8_getbuf);
297 	zassert_equal(time_buf, time_getbuf);
298 	zassert_within(dbl_buf, dbl_getbuf, 0.01);
299 	zassert_equal(strncmp(char_buf, char_getbuf, 10), 0);
300 
301 	ret = lwm2m_delete_object_inst(&LWM2M_OBJ(3303, 0));
302 	zassert_equal(ret, 0);
303 }
304 
ZTEST(lwm2m_registry,test_resource_instance_creation_and_deletion)305 ZTEST(lwm2m_registry, test_resource_instance_creation_and_deletion)
306 {
307 	int ret;
308 
309 	ret = lwm2m_create_res_inst(&LWM2M_OBJ(4, 0, 1, 0));
310 	zassert_equal(ret, 0);
311 
312 	ret = lwm2m_delete_res_inst(&LWM2M_OBJ(4, 0, 1, 0));
313 	zassert_equal(ret, 0);
314 }
315 
ZTEST(lwm2m_registry,test_resource_instance_strings)316 ZTEST(lwm2m_registry, test_resource_instance_strings)
317 {
318 	int ret;
319 	char buf[40] = {0};
320 	static const char string_a[] = "Hello";
321 	static const char string_b[] = "World";
322 	struct lwm2m_obj_path path_a = LWM2M_OBJ(16, 0, 0, 0);
323 	struct lwm2m_obj_path path_b = LWM2M_OBJ(16, 0, 0, 1);
324 
325 	ret = lwm2m_create_object_inst(&LWM2M_OBJ(16, 0));
326 	zassert_equal(ret, 0);
327 
328 	ret = lwm2m_create_res_inst(&path_a);
329 	zassert_equal(ret, 0);
330 
331 	ret = lwm2m_create_res_inst(&path_b);
332 	zassert_equal(ret, 0);
333 
334 	ret = lwm2m_set_string(&path_a, string_a);
335 	zassert_equal(ret, 0);
336 
337 	ret = lwm2m_set_string(&path_b, string_b);
338 	zassert_equal(ret, 0);
339 
340 	ret = lwm2m_get_string(&path_a, buf, sizeof(buf));
341 	zassert_equal(ret, 0);
342 	zassert_equal(0, memcmp(buf, string_a, sizeof(string_a)));
343 
344 	ret = lwm2m_get_string(&path_b, buf, sizeof(buf));
345 	zassert_equal(ret, 0);
346 	zassert_equal(0, memcmp(buf, string_b, sizeof(string_b)));
347 
348 	ret = lwm2m_delete_object_inst(&LWM2M_OBJ(16, 0));
349 	zassert_equal(ret, 0);
350 }
351 
ZTEST(lwm2m_registry,test_callbacks)352 ZTEST(lwm2m_registry, test_callbacks)
353 {
354 	int ret;
355 	double sensor_val;
356 	struct lwm2m_engine_res *exec_res;
357 
358 	callback_checker = 0;
359 	ret = lwm2m_register_create_callback(3303, obj_create_cb);
360 	zassert_equal(ret, 0);
361 	lwm2m_register_delete_callback(3303, obj_delete_cb);
362 	zassert_equal(ret, 0);
363 
364 	ret = lwm2m_create_object_inst(&LWM2M_OBJ(3303, 0));
365 	zassert_equal(ret, 0);
366 	zassert_equal(callback_checker, 0x10);
367 
368 	ret = lwm2m_register_exec_callback(&LWM2M_OBJ(3303, 0, 5605), exec_cb);
369 	zassert_equal(ret, 0);
370 	ret = lwm2m_register_read_callback(&LWM2M_OBJ(3303, 0, 5700), read_cb);
371 	zassert_equal(ret, 0);
372 	ret = lwm2m_register_validate_callback(&LWM2M_OBJ(3303, 0, 5701), validate_cb);
373 	zassert_equal(ret, 0);
374 	ret = lwm2m_register_pre_write_callback(&LWM2M_OBJ(3303, 0, 5701), pre_write_cb);
375 	zassert_equal(ret, 0);
376 	ret = lwm2m_register_post_write_callback(&LWM2M_OBJ(3303, 0, 5701), post_write_cb);
377 	zassert_equal(ret, 0);
378 
379 	exec_res = lwm2m_engine_get_res(&LWM2M_OBJ(3303, 0, 5605));
380 	exec_res->execute_cb(0, 0, 0);
381 
382 	ret = lwm2m_set_string(&LWM2M_OBJ(3303, 0, 5701), "test");
383 	zassert_equal(ret, 0);
384 	zassert_equal(callback_checker, 0x5B);
385 
386 	ret = lwm2m_get_f64(&LWM2M_OBJ(3303, 0, 5700), &sensor_val);
387 	zassert_equal(ret, 0);
388 	zassert_equal(callback_checker, 0x5F);
389 
390 	ret = lwm2m_delete_object_inst(&LWM2M_OBJ(3303, 0));
391 	zassert_equal(ret, 0);
392 	zassert_equal(callback_checker, 0x7F);
393 }
394 
ZTEST(lwm2m_registry,test_strings)395 ZTEST(lwm2m_registry, test_strings)
396 {
397 	int ret;
398 	char buf[40] = {0};
399 	struct lwm2m_obj_path path = LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_STRING);
400 	static const char uri[] = "coap://127.0.0.1";
401 	uint16_t len;
402 	uint8_t *p;
403 
404 	ret = lwm2m_get_res_buf(&path, (void **)&p, &len, NULL, NULL);
405 	zassert_equal(ret, 0);
406 	memset(p, 0xff, len); /* Pre-fill buffer to check */
407 
408 	/* Handle strings in string resources */
409 	ret = lwm2m_set_string(&path, uri);
410 	zassert_equal(ret, 0);
411 	ret = lwm2m_get_res_buf(&path, (void **)&p, NULL, &len, NULL);
412 	zassert_equal(ret, 0);
413 	zassert_equal(len, sizeof(uri));
414 	zassert_equal(p[len - 1], '\0'); /* string terminator in buffer */
415 	zassert_equal(p[len], 0xff);
416 
417 	ret = lwm2m_get_string(&path, buf, sizeof(buf));
418 	zassert_equal(ret, 0);
419 	zassert_equal(memcmp(uri, buf, sizeof(uri)), 0);
420 	ret = lwm2m_get_string(&path, buf, sizeof(uri));
421 	zassert_equal(ret, 0);
422 	ret = lwm2m_get_string(&path, buf, strlen(uri));
423 	zassert_equal(ret, -ENOMEM);
424 
425 	/* Handle strings in opaque resources (no terminator) */
426 	path = LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_OPAQUE);
427 	ret = lwm2m_get_res_buf(&path, (void **)&p, &len, NULL, NULL);
428 	zassert_equal(ret, 0);
429 	memset(p, 0xff, len); /* Pre-fill buffer to check */
430 
431 	ret = lwm2m_set_string(&path, uri);
432 	zassert_equal(ret, 0);
433 	ret = lwm2m_get_res_buf(&path, (void **)&p, NULL, &len, NULL);
434 	zassert_equal(ret, 0);
435 	zassert_equal(len, strlen(uri)); /* No terminator counted in data length */
436 	zassert_equal(p[len - 1], '1'); /* Last character in buffer is not terminator */
437 	zassert_equal(p[len], 0xff);
438 	memset(buf, 0xff, sizeof(buf));
439 	ret = lwm2m_get_string(&path, buf, sizeof(buf)); /* get_string ensures termination */
440 	zassert_equal(ret, 0);
441 	zassert_equal(memcmp(uri, buf, sizeof(uri)), 0);
442 	ret = lwm2m_get_string(&path, buf, sizeof(uri));
443 	zassert_equal(ret, 0);
444 	ret = lwm2m_get_string(&path, buf, strlen(uri));
445 	zassert_equal(ret, -ENOMEM);
446 	/* Corner case: we request exactly as much is stored in opaque resource, */
447 	/* but because we request as a string, it must have room for terminator. */
448 	ret = lwm2m_get_string(&path, buf, len);
449 	zassert_equal(ret, -ENOMEM);
450 }
451 
is_string(const struct lwm2m_obj_path * path)452 static bool is_string(const struct lwm2m_obj_path *path)
453 {
454 	struct lwm2m_engine_obj_field *obj_field;
455 	int ret;
456 
457 	ret = path_to_objs(path, NULL, &obj_field, NULL, NULL);
458 	if (ret < 0 || !obj_field) {
459 		return false;
460 	}
461 	if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
462 		return true;
463 	}
464 	return false;
465 }
466 
test_string_fit(struct lwm2m_obj_path * path,const char * str)467 void test_string_fit(struct lwm2m_obj_path *path, const char *str)
468 {
469 	char buf[40] = {0};
470 	uint8_t *p;
471 	uint16_t len;
472 
473 	zassert_ok(lwm2m_get_res_buf(path, (void **)&p, &len, NULL, NULL));
474 	zassert_equal(len, 32); /* Just check that our test object have 32 byte buffers */
475 	memset(p, 0xff, len);
476 	memset(buf, 0xff, sizeof(buf));
477 
478 	len = strlen(str);
479 	zassert_true((len + 1) >= 31); /* Ensure our test strings fill entire buffer */
480 
481 	/* Test setting and getting a string that fits exactly */
482 	zassert_ok(lwm2m_set_string(path, str));
483 	zassert_ok(lwm2m_get_string(path, buf, sizeof(buf)));
484 	zassert_equal(strlen(buf), len);
485 	zassert_equal(memcmp(buf, str, len + 1), 0); /* check whole buffer, including terminator */
486 	if (is_string(path)) {
487 		zassert_equal(p[len], 0);
488 	} else if (len < 32) {
489 		zassert_equal(p[len], 0xff); /* did not fill entire opaque buffer */
490 	} else {
491 		/* Last byte on resource buffer is last byte from string, no terminator */
492 		zassert_equal((uint8_t) p[31], (uint8_t) str[31]);
493 	}
494 	zassert_equal(buf[len], 0); /* must have terminator */
495 }
496 
ZTEST(lwm2m_registry,test_strings_sizes)497 ZTEST(lwm2m_registry, test_strings_sizes)
498 {
499 	static const char string32[] = "0123456789012345678901234567890";
500 	static const char string33[] = "01234567890123456789012345678901";
501 	static const char string34[] = "012345678901234567890123456789012";
502 	static const char utf8_32[] = "©⏰⏳����������✅";
503 	static const char utf8_33[] = "����������������";
504 	static const char utf8_34[] = "Ω��������������✊";
505 
506 	struct lwm2m_obj_path path_string = LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_STRING);
507 	struct lwm2m_obj_path path_opaque = LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_OPAQUE);
508 
509 	/* All OK without truncation, opaque resource does not store null terminator
510 	 * so it can store one byte more than string resource.
511 	 */
512 	test_string_fit(&path_string, string32);
513 	test_string_fit(&path_opaque, string32);
514 	test_string_fit(&path_opaque, string33);
515 	test_string_fit(&path_string, utf8_32);
516 	test_string_fit(&path_opaque, utf8_32);
517 	test_string_fit(&path_opaque, utf8_33);
518 
519 	/* These would truncate */
520 	zassert_equal(lwm2m_set_string(&path_string, string33), -ENOMEM);
521 	zassert_equal(lwm2m_set_string(&path_opaque, string34), -ENOMEM);
522 	zassert_equal(lwm2m_set_string(&path_string, utf8_33), -ENOMEM);
523 	zassert_equal(lwm2m_set_string(&path_opaque, utf8_34), -ENOMEM);
524 }
525 
ZTEST(lwm2m_registry,test_lock_unlock)526 ZTEST(lwm2m_registry, test_lock_unlock)
527 {
528 	/* Should be recursive mutex and should not block */
529 	lwm2m_registry_lock();
530 	lwm2m_registry_lock();
531 	lwm2m_registry_unlock();
532 	lwm2m_registry_unlock();
533 }
534 
ZTEST(lwm2m_registry,test_resource_wrappers)535 ZTEST(lwm2m_registry, test_resource_wrappers)
536 {
537 	zassert_not_null(lwm2m_engine_obj_list());
538 	zassert_not_null(lwm2m_engine_obj_inst_list());
539 }
540 
ZTEST(lwm2m_registry,test_unregister_obj)541 ZTEST(lwm2m_registry, test_unregister_obj)
542 {
543 
544 	struct lwm2m_obj_path none = {0};
545 	struct lwm2m_engine_obj *obj;
546 
547 	zassert_is_null(lwm2m_engine_get_obj(&none));
548 
549 	obj = lwm2m_engine_get_obj(&LWM2M_OBJ(1));
550 
551 	zassert_not_null(obj);
552 	lwm2m_unregister_obj(obj);
553 	zassert_is_null(lwm2m_engine_get_obj(&LWM2M_OBJ(1)));
554 }
555 
ZTEST(lwm2m_registry,test_next_engine_obj_inst)556 ZTEST(lwm2m_registry, test_next_engine_obj_inst)
557 {
558 	zassert_equal(lwm2m_create_object_inst(&LWM2M_OBJ(3303, 0)), 0);
559 	zassert_equal(lwm2m_create_object_inst(&LWM2M_OBJ(3303, 1)), 0);
560 
561 	struct lwm2m_engine_obj_inst *oi = lwm2m_engine_get_obj_inst(&LWM2M_OBJ(3303, 1));
562 
563 	zassert_not_null(oi);
564 
565 	zassert_not_null(next_engine_obj_inst(3303, 0));
566 	zassert_equal(oi, next_engine_obj_inst(3303, 0));
567 	zassert_is_null(next_engine_obj_inst(3303, 1));
568 
569 	zassert_equal(lwm2m_delete_object_inst(&LWM2M_OBJ(3303, 0)), 0);
570 	zassert_equal(lwm2m_delete_object_inst(&LWM2M_OBJ(3303, 1)), 0);
571 	zassert_is_null(lwm2m_engine_get_obj_inst(&LWM2M_OBJ(3303, 1)));
572 }
573 
ZTEST(lwm2m_registry,test_null_strings)574 ZTEST(lwm2m_registry, test_null_strings)
575 {
576 	int ret;
577 	char buf[40] = {0};
578 	struct lwm2m_obj_path path = LWM2M_OBJ(0, 0, 0);
579 
580 	ret = lwm2m_register_post_write_callback(&path, post_write_cb);
581 	zassert_equal(ret, 0);
582 
583 	callback_checker = 0;
584 	ret = lwm2m_set_string(&path, "string");
585 	zassert_equal(ret, 0);
586 	zassert_equal(callback_checker, 0x02);
587 	ret = lwm2m_get_string(&path, buf, sizeof(buf));
588 	zassert_equal(ret, 0);
589 	zassert_equal(strlen(buf), strlen("string"));
590 
591 	callback_checker = 0;
592 	ret = lwm2m_set_string(&path, "");
593 	zassert_equal(ret, 0);
594 	zassert_equal(callback_checker, 0x02);
595 	ret = lwm2m_get_string(&path, buf, sizeof(buf));
596 	zassert_equal(ret, 0);
597 	zassert_equal(strlen(buf), 0);
598 
599 	callback_checker = 0;
600 	ret = lwm2m_set_opaque(&path, NULL, 0);
601 	zassert_equal(ret, 0);
602 	zassert_equal(callback_checker, 0x02);
603 	ret = lwm2m_get_string(&path, buf, sizeof(buf));
604 	zassert_equal(ret, 0);
605 	zassert_equal(strlen(buf), 0);
606 }
607 
ZTEST(lwm2m_registry,test_obj_version)608 ZTEST(lwm2m_registry, test_obj_version)
609 {
610 #if defined(CONFIG_LWM2M_ENGINE_ALWAYS_REPORT_OBJ_VERSION)
611 	zassert_true(lwm2m_engine_shall_report_obj_version(lwm2m_engine_get_obj(&LWM2M_OBJ(0))));
612 	zassert_true(
613 		lwm2m_engine_shall_report_obj_version(lwm2m_engine_get_obj(&LWM2M_OBJ(32768))));
614 	zassert_true(lwm2m_engine_shall_report_obj_version(lwm2m_engine_get_obj(&LWM2M_OBJ(3303))));
615 #else
616 	zassert_false(lwm2m_engine_shall_report_obj_version(lwm2m_engine_get_obj(&LWM2M_OBJ(0))));
617 	zassert_false(
618 		lwm2m_engine_shall_report_obj_version(lwm2m_engine_get_obj(&LWM2M_OBJ(32768))));
619 	zassert_true(lwm2m_engine_shall_report_obj_version(lwm2m_engine_get_obj(&LWM2M_OBJ(3303))));
620 #endif
621 }
622 
ZTEST(lwm2m_registry,test_resource_cache)623 ZTEST(lwm2m_registry, test_resource_cache)
624 {
625 	struct lwm2m_obj_path path = LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_BOOL);
626 	struct lwm2m_time_series_elem e;
627 
628 	/* Resource cache is turned off */
629 	zassert_is_null(lwm2m_cache_entry_get_by_object(&path));
630 	zassert_equal(lwm2m_enable_cache(&path, &e, 1), -ENOTSUP);
631 	zassert_false(lwm2m_cache_write(NULL, NULL));
632 	zassert_false(lwm2m_cache_read(NULL, NULL));
633 	zassert_equal(lwm2m_cache_size(NULL), 0);
634 }
635 
ZTEST(lwm2m_registry,test_set_bulk)636 ZTEST(lwm2m_registry, test_set_bulk)
637 {
638 	GET_SET_INT((bool), b, true);
639 	GET_SET_ARRAY((uint8_t), opaque, ({0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}));
640 	GET_SET_STRING((char), string, "Hello world");
641 	GET_SET_INT((uint8_t), u8, 80);
642 	GET_SET_INT((int8_t), s8, -80);
643 	GET_SET_INT((uint16_t), u16, 160);
644 	GET_SET_INT((int16_t), s16, -160);
645 	GET_SET_INT((uint32_t), u32, 320);
646 	GET_SET_INT((int32_t), s32, -320);
647 	GET_SET_INT((int64_t), s64, -640);
648 	GET_SET_INT((time_t), t, 1687949518);
649 	GET_SET_INT((double), d, 3.14151);
650 	GET_SET_STRUCT((struct lwm2m_objlnk), objl, ({.obj_id = 10, .obj_inst = 20}));
651 
652 	struct lwm2m_res_item res_items[] = {
653 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_BOOL), &b.out, sizeof(b.out)},
654 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_OPAQUE), opaque.out, sizeof(opaque.out)},
655 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_STRING), string.out, sizeof(string.out)},
656 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_U8), &u8.out, sizeof(u8.out)},
657 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_S8), &s8.out, sizeof(s8.out)},
658 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_U16), &u16.out, sizeof(u16.out)},
659 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_S16), &s16.out, sizeof(s16.out)},
660 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_U32), &u32.out, sizeof(u32.out)},
661 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_S32), &s32.out, sizeof(s32.out)},
662 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_S64), &s64.out, sizeof(s64.out)},
663 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_TIME), &t.out, sizeof(t.out)},
664 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_FLOAT), &d.out, sizeof(d.out)},
665 		{&LWM2M_OBJ(TEST_OBJ_ID, 0, LWM2M_RES_TYPE_OBJLNK), &objl.out, sizeof(objl.out)}};
666 
667 	zassert_equal(lwm2m_set_bulk(res_items, ARRAY_SIZE(res_items)), 0);
668 
669 	/* get all resources */
670 	zassert_ok(lwm2m_get_bool(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_BOOL), &b.in));
671 	zassert_ok(lwm2m_get_opaque(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_OPAQUE), opaque.in,
672 				    sizeof(opaque.in)));
673 	zassert_ok(lwm2m_get_string(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_STRING), string.in,
674 				    sizeof(string.in)));
675 	zassert_ok(lwm2m_get_u8(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U8), &u8.in));
676 	zassert_ok(lwm2m_get_s8(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S8), &s8.in));
677 	zassert_ok(lwm2m_get_u16(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U16), &u16.in));
678 	zassert_ok(lwm2m_get_s16(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S16), &s16.in));
679 	zassert_ok(lwm2m_get_u32(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_U32), &u32.in));
680 	zassert_ok(lwm2m_get_s32(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S32), &s32.in));
681 	zassert_ok(lwm2m_get_s64(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_S64), &s64.in));
682 	zassert_ok(lwm2m_get_time(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_TIME), &t.in));
683 	zassert_ok(lwm2m_get_f64(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_FLOAT), &d.in));
684 	zassert_ok(lwm2m_get_objlnk(&LWM2M_OBJ(32768, 0, LWM2M_RES_TYPE_OBJLNK), &objl.in));
685 
686 	/* check for equality */
687 	zassert_equal(b.in, b.out);
688 	zassert_mem_equal(opaque.in, opaque.out, sizeof(opaque.out));
689 	zassert_str_equal(string.in, string.out);
690 	zassert_equal(u8.in, u8.out);
691 	zassert_equal(s8.in, s8.out);
692 	zassert_equal(u16.in, u16.out);
693 	zassert_equal(s16.in, s16.out);
694 	zassert_equal(u32.in, u32.out);
695 	zassert_equal(s32.in, s32.out);
696 	zassert_equal(s64.in, s64.out);
697 	zassert_equal(t.in, t.out);
698 	zassert_equal(d.in, d.out);
699 	zassert_mem_equal(&objl.in, &objl.out, sizeof(objl.out));
700 }
701