1 /*
2 * Copyright (c) 2018 Laczen
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /** @file
8 * @brief Settings functional test suite
9 *
10 */
11
12 #include <zephyr.h>
13 #include <ztest.h>
14 #include <errno.h>
15 #include <settings/settings.h>
16 #include <logging/log.h>
17 LOG_MODULE_REGISTER(settings_basic_test);
18
19 #if defined(CONFIG_SETTINGS_FCB) || defined(CONFIG_SETTINGS_NVS)
20 #include <storage/flash_map.h>
21 #endif
22 #if IS_ENABLED(CONFIG_SETTINGS_FS)
23 #include <fs/fs.h>
24 #include <fs/littlefs.h>
25 #endif
26
27 /* The standard test expects a cleared flash area. Make sure it has
28 * one.
29 */
test_clear_settings(void)30 static void test_clear_settings(void)
31 {
32 #if IS_ENABLED(CONFIG_SETTINGS_FCB) || IS_ENABLED(CONFIG_SETTINGS_NVS)
33 const struct flash_area *fap;
34 int rc = flash_area_open(FLASH_AREA_ID(storage), &fap);
35
36 if (rc == 0) {
37 rc = flash_area_erase(fap, 0, fap->fa_size);
38 flash_area_close(fap);
39 }
40 zassert_true(rc == 0, "clear settings failed");
41 #endif
42 #if IS_ENABLED(CONFIG_SETTINGS_FS)
43 FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(cstorage);
44
45 /* mounting info */
46 static struct fs_mount_t littlefs_mnt = {
47 .type = FS_LITTLEFS,
48 .fs_data = &cstorage,
49 .storage_dev = (void *)FLASH_AREA_ID(storage),
50 .mnt_point = "/ff"
51 };
52
53 int rc;
54
55 rc = fs_mount(&littlefs_mnt);
56 zassert_true(rc == 0, "mounting littlefs [%d]\n", rc);
57
58 rc = fs_unlink(CONFIG_SETTINGS_FS_FILE);
59 zassert_true(rc == 0 || rc == -ENOENT,
60 "can't delete config file%d\n", rc);
61 #endif
62 }
63
64 /*
65 * Test the two support routines that settings provides:
66 *
67 * settings_name_steq(name, key, next): compares the start of name with key
68 * settings_name_next(name, next): returns the location of the first
69 * separator
70 */
71
test_support_rtn(void)72 static void test_support_rtn(void)
73 {
74 const char test1[] = "bt/a/b/c/d";
75 const char test2[] = "bt/a/b/c/d=";
76 const char *next1, *next2;
77 int rc;
78
79 /* complete match: return 1, next = NULL */
80 rc = settings_name_steq(test1, "bt/a/b/c/d", &next1);
81 zassert_true(rc == 1, "_steq comparison failure");
82 zassert_is_null(next1, "_steq comparison next error");
83 rc = settings_name_steq(test2, "bt/a/b/c/d", &next2);
84 zassert_true(rc == 1, "_steq comparison failure");
85 zassert_is_null(next2, "_steq comparison next error");
86
87 /* partial match: return 1, next <> NULL */
88 rc = settings_name_steq(test1, "bt/a/b/c", &next1);
89 zassert_true(rc == 1, "_steq comparison failure");
90 zassert_not_null(next1, "_steq comparison next error");
91 zassert_equal_ptr(next1, test1+9, "next points to wrong location");
92 rc = settings_name_steq(test2, "bt/a/b/c", &next2);
93 zassert_true(rc == 1, "_steq comparison failure");
94 zassert_not_null(next2, "_steq comparison next error");
95 zassert_equal_ptr(next2, test2+9, "next points to wrong location");
96
97 /* no match: return 0, next = NULL */
98 rc = settings_name_steq(test1, "bta", &next1);
99 zassert_true(rc == 0, "_steq comparison failure");
100 zassert_is_null(next1, "_steq comparison next error");
101 rc = settings_name_steq(test2, "bta", &next2);
102 zassert_true(rc == 0, "_steq comparison failure");
103 zassert_is_null(next2, "_steq comparison next error");
104
105 /* no match: return 0, next = NULL */
106 rc = settings_name_steq(test1, "b", &next1);
107 zassert_true(rc == 0, "_steq comparison failure");
108 zassert_is_null(next1, "_steq comparison next error");
109 rc = settings_name_steq(test2, "b", &next2);
110 zassert_true(rc == 0, "_steq comparison failure");
111 zassert_is_null(next2, "_steq comparison next error");
112
113 /* first separator: return 2, next <> NULL */
114 rc = settings_name_next(test1, &next1);
115 zassert_true(rc == 2, "_next wrong return value");
116 zassert_not_null(next1, "_next wrong next");
117 zassert_equal_ptr(next1, test1+3, "next points to wrong location");
118 rc = settings_name_next(test2, &next2);
119 zassert_true(rc == 2, "_next wrong return value");
120 zassert_not_null(next2, "_next wrong next");
121 zassert_equal_ptr(next2, test2+3, "next points to wrong location");
122
123 /* second separator: return 1, next <> NULL */
124 rc = settings_name_next(next1, &next1);
125 zassert_true(rc == 1, "_next wrong return value");
126 zassert_not_null(next1, "_next wrong next");
127 zassert_equal_ptr(next1, test1+5, "next points to wrong location");
128 rc = settings_name_next(next2, &next2);
129 zassert_true(rc == 1, "_next wrong return value");
130 zassert_not_null(next2, "_next wrong next");
131 zassert_equal_ptr(next2, test2+5, "next points to wrong location");
132
133 /* third separator: return 1, next <> NULL */
134 rc = settings_name_next(next1, &next1);
135 zassert_true(rc == 1, "_next wrong return value");
136 zassert_not_null(next1, "_next wrong next");
137 rc = settings_name_next(next2, &next2);
138 zassert_true(rc == 1, "_next wrong return value");
139 zassert_not_null(next2, "_next wrong next");
140
141 /* fourth separator: return 1, next <> NULL */
142 rc = settings_name_next(next1, &next1);
143 zassert_true(rc == 1, "_next wrong return value");
144 zassert_not_null(next1, "_next wrong next");
145 rc = settings_name_next(next2, &next2);
146 zassert_true(rc == 1, "_next wrong return value");
147 zassert_not_null(next2, "_next wrong next");
148
149 /* fifth separator: return 1, next == NULL */
150 rc = settings_name_next(next1, &next1);
151 zassert_true(rc == 1, "_next wrong return value");
152 zassert_is_null(next1, "_next wrong next");
153 rc = settings_name_next(next2, &next2);
154 zassert_true(rc == 1, "_next wrong return value");
155 zassert_is_null(next2, "_next wrong next");
156
157 }
158
159 struct stored_data {
160 uint8_t val1;
161 uint8_t val2;
162 uint8_t val3;
163 bool en1;
164 bool en2;
165 bool en3;
166 };
167
168 struct stored_data data;
169
val1_set(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg)170 int val1_set(const char *key, size_t len, settings_read_cb read_cb,
171 void *cb_arg)
172 {
173 data.val1 = 1;
174 return 0;
175 }
val1_commit(void)176 int val1_commit(void)
177 {
178 data.en1 = true;
179 return 0;
180 }
181 static struct settings_handler val1_settings = {
182 .name = "ps",
183 .h_set = val1_set,
184 .h_commit = val1_commit,
185 };
186
val2_set(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg)187 int val2_set(const char *key, size_t len, settings_read_cb read_cb,
188 void *cb_arg)
189 {
190 data.val2 = 2;
191 return 0;
192 }
val2_commit(void)193 int val2_commit(void)
194 {
195 data.en2 = true;
196 return 0;
197 }
198 static struct settings_handler val2_settings = {
199 .name = "ps/ss/ss",
200 .h_set = val2_set,
201 .h_commit = val2_commit,
202 };
203
val3_set(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg)204 int val3_set(const char *key, size_t len, settings_read_cb read_cb,
205 void *cb_arg)
206 {
207 data.val3 = 3;
208 return 0;
209 }
val3_commit(void)210 int val3_commit(void)
211 {
212 data.en3 = true;
213 return 0;
214 }
215 static struct settings_handler val3_settings = {
216 .name = "ps/ss",
217 .h_set = val3_set,
218 .h_commit = val3_commit,
219 };
220
221 /* helper routine to remove a handler from settings */
settings_deregister(struct settings_handler * handler)222 int settings_deregister(struct settings_handler *handler)
223 {
224 extern sys_slist_t settings_handlers;
225
226 return sys_slist_find_and_remove(&settings_handlers, &handler->node);
227 }
228
test_register_and_loading(void)229 static void test_register_and_loading(void)
230 {
231 int rc, err;
232 uint8_t val = 0;
233
234 rc = settings_subsys_init();
235 zassert_true(rc == 0, "subsys init failed");
236
237
238 settings_save_one("ps/ss/ss/val2", &val, sizeof(uint8_t));
239
240 memset(&data, 0, sizeof(struct stored_data));
241
242 rc = settings_register(&val1_settings);
243 zassert_true(rc == 0, "register of val1 settings failed");
244
245 /* when we load settings now data.val1 should receive the value*/
246 rc = settings_load();
247 zassert_true(rc == 0, "settings_load failed");
248 err = (data.val1 == 1) && (data.val2 == 0) && (data.val3 == 0);
249 zassert_true(err, "wrong data value found");
250 /* commit is only called for val1_settings */
251 err = (data.en1) && (!data.en2) && (!data.en3);
252 zassert_true(err, "wrong data enable found");
253
254 /* Next register should be ok */
255 rc = settings_register(&val2_settings);
256 zassert_true(rc == 0, "register of val2 settings failed");
257
258 /* Next register should fail (same name) */
259 rc = settings_register(&val2_settings);
260 zassert_true(rc == -EEXIST, "double register of val2 settings allowed");
261
262 memset(&data, 0, sizeof(struct stored_data));
263 /* when we load settings now data.val2 should receive the value*/
264 rc = settings_load();
265 zassert_true(rc == 0, "settings_load failed");
266 err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 0);
267 zassert_true(err, "wrong data value found");
268 /* commit is called for val1_settings and val2_settings*/
269 err = (data.en1) && (data.en2) && (!data.en3);
270 zassert_true(err, "wrong data enable found");
271
272 settings_save_one("ps/ss/val3", &val, sizeof(uint8_t));
273 memset(&data, 0, sizeof(struct stored_data));
274 /* when we load settings now data.val2 and data.val1 should receive a
275 * value
276 */
277 rc = settings_load();
278 zassert_true(rc == 0, "settings_load failed");
279 err = (data.val1 == 1) && (data.val2 == 2) && (data.val3 == 0);
280 zassert_true(err, "wrong data value found");
281 /* commit is called for val1_settings and val2_settings*/
282 err = (data.en1) && (data.en2) && (!data.en3);
283 zassert_true(err, "wrong data enable found");
284
285 /* val3 settings should be inserted in between val1_settings and
286 * val2_settings
287 */
288 rc = settings_register(&val3_settings);
289 zassert_true(rc == 0, "register of val3 settings failed");
290 memset(&data, 0, sizeof(struct stored_data));
291 /* when we load settings now data.val2 and data.val3 should receive a
292 * value
293 */
294 rc = settings_load();
295 zassert_true(rc == 0, "settings_load failed");
296 err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 3);
297 zassert_true(err, "wrong data value found");
298 /* commit is called for val1_settings, val2_settings and val3_settings
299 */
300 err = (data.en1) && (data.en2) && (data.en3);
301 zassert_true(err, "wrong data enable found");
302
303 settings_save_one("ps/val1", &val, sizeof(uint8_t));
304 memset(&data, 0, sizeof(struct stored_data));
305 /* when we load settings all data should receive a value loaded */
306 rc = settings_load();
307 zassert_true(rc == 0, "settings_load failed");
308 err = (data.val1 == 1) && (data.val2 == 2) && (data.val3 == 3);
309 zassert_true(err, "wrong data value found");
310 /* commit is called for all settings*/
311 err = (data.en1) && (data.en2) && (data.en3);
312 zassert_true(err, "wrong data enable found");
313
314 memset(&data, 0, sizeof(struct stored_data));
315 /* test subtree loading: subtree "ps/ss" data.val2 and data.val3 should
316 * receive a value
317 */
318 rc = settings_load_subtree("ps/ss");
319 zassert_true(rc == 0, "settings_load failed");
320 err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 3);
321 zassert_true(err, "wrong data value found");
322 /* commit is called for val2_settings and val3_settings */
323 err = (!data.en1) && (data.en2) && (data.en3);
324 zassert_true(err, "wrong data enable found");
325
326 memset(&data, 0, sizeof(struct stored_data));
327 /* test subtree loading: subtree "ps/ss/ss" only data.val2 should
328 * receive a value
329 */
330 rc = settings_load_subtree("ps/ss/ss");
331 zassert_true(rc == 0, "settings_load failed");
332 err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 0);
333 zassert_true(err, "wrong data value found");
334 /* commit is called only for val2_settings */
335 err = (!data.en1) && (data.en2) && (!data.en3);
336 zassert_true(err, "wrong data enable found");
337
338 /* clean up by deregisterring settings_handler */
339 rc = settings_deregister(&val1_settings);
340 zassert_true(rc, "deregistering val1_settings failed");
341
342 rc = settings_deregister(&val2_settings);
343 zassert_true(rc, "deregistering val1_settings failed");
344
345 rc = settings_deregister(&val3_settings);
346 zassert_true(rc, "deregistering val1_settings failed");
347 }
348
val123_set(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg)349 int val123_set(const char *key, size_t len,
350 settings_read_cb read_cb, void *cb_arg)
351 {
352 int rc;
353 uint8_t val;
354
355 zassert_equal(1, len, "Unexpected size");
356
357 rc = read_cb(cb_arg, &val, sizeof(val));
358 zassert_equal(sizeof(val), rc, "read_cb failed");
359
360 if (!strcmp("1", key)) {
361 data.val1 = val;
362 data.en1 = true;
363 return 0;
364 }
365 if (!strcmp("2", key)) {
366 data.val2 = val;
367 data.en2 = true;
368 return 0;
369 }
370 if (!strcmp("3", key)) {
371 data.val3 = val;
372 data.en3 = true;
373 return 0;
374 }
375
376 zassert_unreachable("Unexpected key value: %s", key);
377
378 return 0;
379 }
380
381 static struct settings_handler val123_settings = {
382 .name = "val",
383 .h_set = val123_set,
384 };
385
386 unsigned int direct_load_cnt;
387 uint8_t val_directly_loaded;
388
direct_loader(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)389 int direct_loader(
390 const char *key,
391 size_t len,
392 settings_read_cb read_cb,
393 void *cb_arg,
394 void *param)
395 {
396 int rc;
397 uint8_t val;
398
399 zassert_equal(0x1234, (size_t)param, NULL);
400
401 zassert_equal(1, len, NULL);
402 zassert_is_null(key, "Unexpected key: %s", key);
403
404
405 zassert_not_null(cb_arg, NULL);
406 rc = read_cb(cb_arg, &val, sizeof(val));
407 zassert_equal(sizeof(val), rc, NULL);
408
409 val_directly_loaded = val;
410 direct_load_cnt += 1;
411 return 0;
412 }
413
414
test_direct_loading(void)415 static void test_direct_loading(void)
416 {
417 int rc;
418 uint8_t val;
419
420 val = 11;
421 settings_save_one("val/1", &val, sizeof(uint8_t));
422 val = 23;
423 settings_save_one("val/2", &val, sizeof(uint8_t));
424 val = 35;
425 settings_save_one("val/3", &val, sizeof(uint8_t));
426
427 rc = settings_register(&val123_settings);
428 zassert_true(rc == 0, NULL);
429 memset(&data, 0, sizeof(data));
430
431 rc = settings_load();
432 zassert_true(rc == 0, NULL);
433
434 zassert_equal(11, data.val1, NULL);
435 zassert_equal(23, data.val2, NULL);
436 zassert_equal(35, data.val3, NULL);
437
438 /* Load subtree */
439 memset(&data, 0, sizeof(data));
440
441 rc = settings_load_subtree("val/2");
442 zassert_true(rc == 0, NULL);
443
444 zassert_equal(0, data.val1, NULL);
445 zassert_equal(23, data.val2, NULL);
446 zassert_equal(0, data.val3, NULL);
447
448 /* Direct loading now */
449 memset(&data, 0, sizeof(data));
450 val_directly_loaded = 0;
451 direct_load_cnt = 0;
452 rc = settings_load_subtree_direct(
453 "val/2",
454 direct_loader,
455 (void *)0x1234);
456 zassert_true(rc == 0, NULL);
457 zassert_equal(0, data.val1, NULL);
458 zassert_equal(0, data.val2, NULL);
459 zassert_equal(0, data.val3, NULL);
460
461 zassert_equal(1, direct_load_cnt, NULL);
462 zassert_equal(23, val_directly_loaded, NULL);
463 }
464
465 struct test_loading_data {
466 const char *n;
467 const char *v;
468 };
469
470 /* Final data */
471 static const struct test_loading_data data_final[] = {
472 { .n = "val/1", .v = "final 1" },
473 { .n = "val/2", .v = "final 2" },
474 { .n = "val/3", .v = "final 3" },
475 { .n = "val/4", .v = "final 4" },
476 { .n = NULL }
477 };
478
479 /* The counter of the callback called */
480 static unsigned int data_final_called[ARRAY_SIZE(data_final)];
481
482
filtered_loader(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg)483 static int filtered_loader(
484 const char *key,
485 size_t len,
486 settings_read_cb read_cb,
487 void *cb_arg)
488 {
489 int rc;
490 const char *next;
491 char buf[32];
492 const struct test_loading_data *ldata;
493
494 printk("-- Called: %s\n", key);
495
496 /* Searching for a element in an array */
497 for (ldata = data_final; ldata->n; ldata += 1) {
498 if (settings_name_steq(key, ldata->n, &next)) {
499 break;
500 }
501 }
502 zassert_not_null(ldata->n, "Unexpected data name: %s", key);
503 zassert_is_null(next, NULL);
504 zassert_equal(strlen(ldata->v) + 1, len, "e: \"%s\", a:\"%s\"", ldata->v, buf);
505 zassert_true(len <= sizeof(buf), NULL);
506
507 rc = read_cb(cb_arg, buf, len);
508 zassert_equal(len, rc, NULL);
509
510 zassert_false(strcmp(ldata->v, buf), "e: \"%s\", a:\"%s\"", ldata->v, buf);
511
512 /* Count an element that was properly loaded */
513 data_final_called[ldata - data_final] += 1;
514
515 return 0;
516 }
517
518 static struct settings_handler filtered_loader_settings = {
519 .name = "filtered_test",
520 .h_set = filtered_loader,
521 };
522
523
direct_filtered_loader(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)524 static int direct_filtered_loader(
525 const char *key,
526 size_t len,
527 settings_read_cb read_cb,
528 void *cb_arg,
529 void *param)
530 {
531 zassert_equal(0x3456, (size_t)param, NULL);
532 return filtered_loader(key, len, read_cb, cb_arg);
533 }
534
535
test_direct_loading_filter(void)536 static void test_direct_loading_filter(void)
537 {
538 int rc;
539 const struct test_loading_data *ldata;
540 const char *prefix = filtered_loader_settings.name;
541 char buffer[48];
542 size_t n;
543
544 /* Duplicated data */
545 static const struct test_loading_data data_duplicates[] = {
546 { .n = "val/1", .v = "dup abc" },
547 { .n = "val/2", .v = "dup 123" },
548 { .n = "val/3", .v = "dup 11" },
549 { .n = "val/4", .v = "dup 34" },
550 { .n = "val/1", .v = "dup 56" },
551 { .n = "val/2", .v = "dup 7890" },
552 { .n = "val/4", .v = "dup niety" },
553 { .n = "val/3", .v = "dup er" },
554 { .n = "val/3", .v = "dup super" },
555 { .n = "val/3", .v = "dup xxx" },
556 { .n = NULL }
557 };
558
559 /* Data that is going to be deleted */
560 strcpy(buffer, prefix);
561 strcat(buffer, "/to_delete");
562 settings_save_one(buffer, "1", 2);
563 (void) settings_delete(buffer);
564
565 /* Saving all the data */
566 for (ldata = data_duplicates; ldata->n; ++ldata) {
567 strcpy(buffer, prefix);
568 strcat(buffer, "/");
569 strcat(buffer, ldata->n);
570 settings_save_one(buffer, ldata->v, strlen(ldata->v) + 1);
571 }
572 for (ldata = data_final; ldata->n; ++ldata) {
573 strcpy(buffer, prefix);
574 strcat(buffer, "/");
575 strcat(buffer, ldata->n);
576 settings_save_one(buffer, ldata->v, strlen(ldata->v) + 1);
577 }
578
579
580 memset(data_final_called, 0, sizeof(data_final_called));
581
582 rc = settings_load_subtree_direct(
583 prefix,
584 direct_filtered_loader,
585 (void *)0x3456);
586 zassert_equal(0, rc, NULL);
587
588 /* Check if all the data was called */
589 for (n = 0; data_final[n].n; ++n) {
590 zassert_equal(1, data_final_called[n],
591 "Unexpected number of calls (%u) of (%s) element",
592 n, data_final[n].n);
593 }
594
595 rc = settings_register(&filtered_loader_settings);
596 zassert_true(rc == 0, NULL);
597
598 rc = settings_load_subtree(prefix);
599 zassert_equal(0, rc, NULL);
600
601 /* Check if all the data was called */
602 for (n = 0; data_final[n].n; ++n) {
603 zassert_equal(2, data_final_called[n],
604 "Unexpected number of calls (%u) of (%s) element",
605 n, data_final[n].n);
606 }
607 }
608
609
test_main(void)610 void test_main(void)
611 {
612 ztest_test_suite(settings_test_suite,
613 ztest_unit_test(test_clear_settings),
614 ztest_unit_test(test_support_rtn),
615 ztest_unit_test(test_register_and_loading),
616 ztest_unit_test(test_direct_loading),
617 ztest_unit_test(test_direct_loading_filter)
618 );
619
620 ztest_run_test_suite(settings_test_suite);
621 }
622