/* * Copyright (c) 2018 Laczen * Copyright (c) 2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ /** @file * @brief Settings functional test suite * */ #include #include #include #include LOG_MODULE_REGISTER(settings_basic_test); #if defined(CONFIG_SETTINGS_FCB) || defined(CONFIG_SETTINGS_NVS) || defined(CONFIG_SETTINGS_ZMS) #include #if DT_HAS_CHOSEN(zephyr_settings_partition) #define TEST_FLASH_AREA_ID DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_settings_partition)) #endif #elif defined(CONFIG_SETTINGS_FILE) #include #include #elif defined(CONFIG_SETTINGS_TFM_PSA) #if defined(CONFIG_SETTINGS_TFM_PSA_BACKEND_PS) #include #include #define SETTINGS_PSA_MAX_ASSET_SIZE PS_MAX_ASSET_SIZE #define SETTINGS_PSA_REMOVE psa_ps_remove #define SETTINGS_PSA_ID_RANGE_START ZEPHYR_PSA_SETTINGS_TFM_PS_UID_RANGE_BEGIN #elif defined(CONFIG_SETTINGS_TFM_PSA_BACKEND_ITS) #include #include #define SETTINGS_PSA_MAX_ASSET_SIZE ITS_MAX_ASSET_SIZE #define SETTINGS_PSA_REMOVE psa_its_remove #define SETTINGS_PSA_ID_RANGE_START ZEPHYR_PSA_SETTINGS_TFM_ITS_UID_RANGE_BEGIN #else #error "No PSA backend selected" #endif /* CONFIG_SETTINGS_TFM_PSA_BACKEND */ /* TF-M config file containing ITS_MAX_ASSET_SIZE */ #include #include #else #error "Settings backend not selected" #endif #ifndef TEST_FLASH_AREA_ID #define TEST_FLASH_AREA storage_partition #define TEST_FLASH_AREA_ID FIXED_PARTITION_ID(TEST_FLASH_AREA) #endif /* The standard test expects a cleared flash area. Make sure it has * one. */ ZTEST(settings_functional, test_clear_settings) { #if defined(CONFIG_SETTINGS_TFM_PSA) psa_status_t status; /* Remove all potentially accessed entries in the UID range */ for (int i = 0; i < sizeof(struct setting_entry) * CONFIG_SETTINGS_TFM_PSA_NUM_ENTRIES / SETTINGS_PSA_MAX_ASSET_SIZE + 1; i++) { status = SETTINGS_PSA_REMOVE(SETTINGS_PSA_ID_RANGE_START + i); zassert_true((status == PSA_SUCCESS) || (status == PSA_ERROR_DOES_NOT_EXIST), "psa_its_remove failed"); } #elif !defined(CONFIG_SETTINGS_FILE) const struct flash_area *fap; int rc; rc = flash_area_open(TEST_FLASH_AREA_ID, &fap); if (rc == 0) { rc = flash_area_flatten(fap, 0, fap->fa_size); flash_area_close(fap); } zassert_true(rc == 0, "clear settings failed"); #else FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(cstorage); /* mounting info */ static struct fs_mount_t littlefs_mnt = { .type = FS_LITTLEFS, .fs_data = &cstorage, .storage_dev = (void *)TEST_FLASH_AREA_ID, .mnt_point = "/ff" }; int rc; rc = fs_mount(&littlefs_mnt); zassert_true(rc == 0, "mounting littlefs [%d]\n", rc); rc = fs_unlink(CONFIG_SETTINGS_FILE_PATH); zassert_true(rc == 0 || rc == -ENOENT, "can't delete config file%d\n", rc); #endif } /* * Test the two support routines that settings provides: * * settings_name_steq(name, key, next): compares the start of name with key * settings_name_next(name, next): returns the location of the first * separator */ ZTEST(settings_functional, test_support_rtn) { const char test1[] = "bt/a/b/c/d"; const char test2[] = "bt/a/b/c/d="; const char *next1, *next2; int rc; /* complete match: return 1, next = NULL */ rc = settings_name_steq(test1, "bt/a/b/c/d", &next1); zassert_true(rc == 1, "_steq comparison failure"); zassert_is_null(next1, "_steq comparison next error"); rc = settings_name_steq(test2, "bt/a/b/c/d", &next2); zassert_true(rc == 1, "_steq comparison failure"); zassert_is_null(next2, "_steq comparison next error"); /* partial match: return 1, next <> NULL */ rc = settings_name_steq(test1, "bt/a/b/c", &next1); zassert_true(rc == 1, "_steq comparison failure"); zassert_not_null(next1, "_steq comparison next error"); zassert_equal_ptr(next1, test1+9, "next points to wrong location"); rc = settings_name_steq(test2, "bt/a/b/c", &next2); zassert_true(rc == 1, "_steq comparison failure"); zassert_not_null(next2, "_steq comparison next error"); zassert_equal_ptr(next2, test2+9, "next points to wrong location"); /* no match: return 0, next = NULL */ rc = settings_name_steq(test1, "bta", &next1); zassert_true(rc == 0, "_steq comparison failure"); zassert_is_null(next1, "_steq comparison next error"); rc = settings_name_steq(test2, "bta", &next2); zassert_true(rc == 0, "_steq comparison failure"); zassert_is_null(next2, "_steq comparison next error"); /* no match: return 0, next = NULL */ rc = settings_name_steq(test1, "b", &next1); zassert_true(rc == 0, "_steq comparison failure"); zassert_is_null(next1, "_steq comparison next error"); rc = settings_name_steq(test2, "b", &next2); zassert_true(rc == 0, "_steq comparison failure"); zassert_is_null(next2, "_steq comparison next error"); /* first separator: return 2, next <> NULL */ rc = settings_name_next(test1, &next1); zassert_true(rc == 2, "_next wrong return value"); zassert_not_null(next1, "_next wrong next"); zassert_equal_ptr(next1, test1+3, "next points to wrong location"); rc = settings_name_next(test2, &next2); zassert_true(rc == 2, "_next wrong return value"); zassert_not_null(next2, "_next wrong next"); zassert_equal_ptr(next2, test2+3, "next points to wrong location"); /* second separator: return 1, next <> NULL */ rc = settings_name_next(next1, &next1); zassert_true(rc == 1, "_next wrong return value"); zassert_not_null(next1, "_next wrong next"); zassert_equal_ptr(next1, test1+5, "next points to wrong location"); rc = settings_name_next(next2, &next2); zassert_true(rc == 1, "_next wrong return value"); zassert_not_null(next2, "_next wrong next"); zassert_equal_ptr(next2, test2+5, "next points to wrong location"); /* third separator: return 1, next <> NULL */ rc = settings_name_next(next1, &next1); zassert_true(rc == 1, "_next wrong return value"); zassert_not_null(next1, "_next wrong next"); rc = settings_name_next(next2, &next2); zassert_true(rc == 1, "_next wrong return value"); zassert_not_null(next2, "_next wrong next"); /* fourth separator: return 1, next <> NULL */ rc = settings_name_next(next1, &next1); zassert_true(rc == 1, "_next wrong return value"); zassert_not_null(next1, "_next wrong next"); rc = settings_name_next(next2, &next2); zassert_true(rc == 1, "_next wrong return value"); zassert_not_null(next2, "_next wrong next"); /* fifth separator: return 1, next == NULL */ rc = settings_name_next(next1, &next1); zassert_true(rc == 1, "_next wrong return value"); zassert_is_null(next1, "_next wrong next"); rc = settings_name_next(next2, &next2); zassert_true(rc == 1, "_next wrong return value"); zassert_is_null(next2, "_next wrong next"); } struct stored_data { uint8_t val1; uint8_t val2; uint8_t val3; bool en1; bool en2; bool en3; }; struct stored_data data; int val1_set(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg) { data.val1 = 1; return 0; } int val1_commit(void) { data.en1 = true; return 0; } static struct settings_handler val1_settings = { .name = "ps", .h_set = val1_set, .h_commit = val1_commit, }; int val2_set(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg) { data.val2 = 2; return 0; } int val2_commit(void) { data.en2 = true; return 0; } static struct settings_handler val2_settings = { .name = "ps/ss/ss", .h_set = val2_set, .h_commit = val2_commit, }; int val3_set(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg) { data.val3 = 3; return 0; } int val3_commit(void) { data.en3 = true; return 0; } static struct settings_handler val3_settings = { .name = "ps/ss", .h_set = val3_set, .h_commit = val3_commit, }; /* helper routine to remove a handler from settings */ int settings_deregister(struct settings_handler *handler) { extern sys_slist_t settings_handlers; return sys_slist_find_and_remove(&settings_handlers, &handler->node); } ZTEST(settings_functional, test_register_and_loading) { int rc, err; uint8_t val = 0; ssize_t val_len = 0; rc = settings_subsys_init(); zassert_true(rc == 0, "subsys init failed"); /* Check that key that corresponds to val2 do not exist in storage */ val_len = settings_get_val_len("ps/ss/ss/val2"); zassert_true((val_len == 0), "Failure: key should not exist"); settings_save_one("ps/ss/ss/val2", &val, sizeof(uint8_t)); /* Check that the key that corresponds to val2 exists in storage */ val_len = settings_get_val_len("ps/ss/ss/val2"); zassert_true((val_len == 1), "Failure: key should exist"); memset(&data, 0, sizeof(struct stored_data)); rc = settings_register(&val1_settings); zassert_true(rc == 0, "register of val1 settings failed"); /* when we load settings now data.val1 should receive the value*/ rc = settings_load(); zassert_true(rc == 0, "settings_load failed"); err = (data.val1 == 1) && (data.val2 == 0) && (data.val3 == 0); zassert_true(err, "wrong data value found"); /* commit is only called for val1_settings */ err = (data.en1) && (!data.en2) && (!data.en3); zassert_true(err, "wrong data enable found"); /* Next register should be ok */ rc = settings_register(&val2_settings); zassert_true(rc == 0, "register of val2 settings failed"); /* Next register should fail (same name) */ rc = settings_register(&val2_settings); zassert_true(rc == -EEXIST, "double register of val2 settings allowed"); memset(&data, 0, sizeof(struct stored_data)); /* when we load settings now data.val2 should receive the value*/ rc = settings_load(); zassert_true(rc == 0, "settings_load failed"); err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 0); zassert_true(err, "wrong data value found"); /* commit is called for val1_settings and val2_settings*/ err = (data.en1) && (data.en2) && (!data.en3); zassert_true(err, "wrong data enable found"); /* Check that key that corresponds to val3 do not exist in storage */ val_len = settings_get_val_len("ps/ss/val3"); zassert_true((val_len == 0), "Failure: key should not exist"); settings_save_one("ps/ss/val3", &val, sizeof(uint8_t)); /* Check that the key that corresponds to val3 exists in storage */ val_len = settings_get_val_len("ps/ss/val3"); zassert_true((val_len == 1), "Failure: key should exist"); memset(&data, 0, sizeof(struct stored_data)); /* when we load settings now data.val2 and data.val1 should receive a * value */ rc = settings_load(); zassert_true(rc == 0, "settings_load failed"); err = (data.val1 == 1) && (data.val2 == 2) && (data.val3 == 0); zassert_true(err, "wrong data value found"); /* commit is called for val1_settings and val2_settings*/ err = (data.en1) && (data.en2) && (!data.en3); zassert_true(err, "wrong data enable found"); /* val3 settings should be inserted in between val1_settings and * val2_settings */ rc = settings_register(&val3_settings); zassert_true(rc == 0, "register of val3 settings failed"); memset(&data, 0, sizeof(struct stored_data)); /* when we load settings now data.val2 and data.val3 should receive a * value */ rc = settings_load(); zassert_true(rc == 0, "settings_load failed"); err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 3); zassert_true(err, "wrong data value found"); /* commit is called for val1_settings, val2_settings and val3_settings */ err = (data.en1) && (data.en2) && (data.en3); zassert_true(err, "wrong data enable found"); /* Check that key that corresponds to val1 do not exist in storage */ val_len = settings_get_val_len("ps/val1"); zassert_true((val_len == 0), "Failure: key should not exist"); settings_save_one("ps/val1", &val, sizeof(uint8_t)); /* Check that the key that corresponds to val1 exists in storage */ val_len = settings_get_val_len("ps/val1"); zassert_true((val_len == 1), "Failure: key should exist"); memset(&data, 0, sizeof(struct stored_data)); /* when we load settings all data should receive a value loaded */ rc = settings_load(); zassert_true(rc == 0, "settings_load failed"); err = (data.val1 == 1) && (data.val2 == 2) && (data.val3 == 3); zassert_true(err, "wrong data value found"); /* commit is called for all settings*/ err = (data.en1) && (data.en2) && (data.en3); zassert_true(err, "wrong data enable found"); memset(&data, 0, sizeof(struct stored_data)); /* test subtree loading: subtree "ps/ss" data.val2 and data.val3 should * receive a value */ rc = settings_load_subtree("ps/ss"); zassert_true(rc == 0, "settings_load failed"); err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 3); zassert_true(err, "wrong data value found"); /* commit is called for val2_settings and val3_settings */ err = (!data.en1) && (data.en2) && (data.en3); zassert_true(err, "wrong data enable found"); memset(&data, 0, sizeof(struct stored_data)); /* test subtree loading: subtree "ps/ss/ss" only data.val2 should * receive a value */ rc = settings_load_subtree("ps/ss/ss"); zassert_true(rc == 0, "settings_load failed"); err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 0); zassert_true(err, "wrong data value found"); /* commit is called only for val2_settings */ err = (!data.en1) && (data.en2) && (!data.en3); zassert_true(err, "wrong data enable found"); memset(&data, 0, sizeof(struct stored_data)); /* test load_one: path "ps/ss/ss/val2". Only data.val2 should * receive a value */ val = 2; settings_save_one("ps/ss/ss/val2", &val, sizeof(uint8_t)); rc = settings_load_one("ps/ss/ss/val2", &data.val2, sizeof(uint8_t)); zassert_true(rc >= 0, "settings_load_one failed"); err = (data.val1 == 0) && (data.val2 == 2) && (data.val3 == 0); zassert_true(err, "wrong data value found %u != 2", data.val2); /* clean up by deregistering settings_handler */ rc = settings_deregister(&val1_settings); zassert_true(rc, "deregistering val1_settings failed"); rc = settings_deregister(&val2_settings); zassert_true(rc, "deregistering val2_settings failed"); rc = settings_deregister(&val3_settings); zassert_true(rc, "deregistering val3_settings failed"); } int val123_set(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg) { int rc; uint8_t val; zassert_equal(1, len, "Unexpected size"); rc = read_cb(cb_arg, &val, sizeof(val)); zassert_equal(sizeof(val), rc, "read_cb failed"); if (!strcmp("1", key)) { data.val1 = val; data.en1 = true; return 0; } if (!strcmp("2", key)) { data.val2 = val; data.en2 = true; return 0; } if (!strcmp("3", key)) { data.val3 = val; data.en3 = true; return 0; } zassert_unreachable("Unexpected key value: %s", key); return 0; } static struct settings_handler val123_settings = { .name = "val", .h_set = val123_set, }; unsigned int direct_load_cnt; uint8_t val_directly_loaded; int direct_loader( const char *key, size_t len, settings_read_cb read_cb, void *cb_arg, void *param) { int rc; uint8_t val; zassert_equal(0x1234, (size_t)param); zassert_equal(1, len); zassert_is_null(key, "Unexpected key: %s", key); zassert_not_null(cb_arg); rc = read_cb(cb_arg, &val, sizeof(val)); zassert_equal(sizeof(val), rc); val_directly_loaded = val; direct_load_cnt += 1; return 0; } ZTEST(settings_functional, test_direct_loading) { int rc; uint8_t val; settings_subsys_init(); val = 11; settings_save_one("val/1", &val, sizeof(uint8_t)); val = 23; settings_save_one("val/2", &val, sizeof(uint8_t)); val = 35; settings_save_one("val/3", &val, sizeof(uint8_t)); rc = settings_register(&val123_settings); zassert_true(rc == 0); memset(&data, 0, sizeof(data)); rc = settings_load(); zassert_true(rc == 0); zassert_equal(11, data.val1); zassert_equal(23, data.val2); zassert_equal(35, data.val3); /* Load subtree */ memset(&data, 0, sizeof(data)); rc = settings_load_subtree("val/2"); zassert_true(rc == 0); zassert_equal(0, data.val1); zassert_equal(23, data.val2); zassert_equal(0, data.val3); /* Direct loading now */ memset(&data, 0, sizeof(data)); val_directly_loaded = 0; direct_load_cnt = 0; rc = settings_load_subtree_direct( "val/2", direct_loader, (void *)0x1234); zassert_true(rc == 0); zassert_equal(0, data.val1); zassert_equal(0, data.val2); zassert_equal(0, data.val3); zassert_equal(1, direct_load_cnt); zassert_equal(23, val_directly_loaded); settings_deregister(&val123_settings); } struct test_loading_data { const char *n; const char *v; }; /* Final data */ static const struct test_loading_data data_final[] = { { .n = "val/1", .v = "final 1" }, { .n = "val/2", .v = "final 2" }, { .n = "val/3", .v = "final 3" }, { .n = "val/4", .v = "final 4" }, { .n = NULL } }; /* The counter of the callback called */ static unsigned int data_final_called[ARRAY_SIZE(data_final)]; static int filtered_loader( const char *key, size_t len, settings_read_cb read_cb, void *cb_arg) { int rc; const char *next; char buf[32]; const struct test_loading_data *ldata; printk("-- Called: %s\n", key); /* Searching for a element in an array */ for (ldata = data_final; ldata->n; ldata += 1) { if (settings_name_steq(key, ldata->n, &next)) { break; } } zassert_not_null(ldata->n, "Unexpected data name: %s", key); zassert_is_null(next); zassert_equal(strlen(ldata->v) + 1, len, "e: \"%s\", a:\"%s\"", ldata->v, buf); zassert_true(len <= sizeof(buf)); rc = read_cb(cb_arg, buf, len); zassert_equal(len, rc); zassert_false(strcmp(ldata->v, buf), "e: \"%s\", a:\"%s\"", ldata->v, buf); /* Count an element that was properly loaded */ data_final_called[ldata - data_final] += 1; return 0; } static struct settings_handler filtered_loader_settings = { .name = "filtered_test", .h_set = filtered_loader, }; static int direct_filtered_loader( const char *key, size_t len, settings_read_cb read_cb, void *cb_arg, void *param) { zassert_equal(0x3456, (size_t)param); return filtered_loader(key, len, read_cb, cb_arg); } ZTEST(settings_functional, test_direct_loading_filter) { int rc; const struct test_loading_data *ldata; const char *prefix = filtered_loader_settings.name; char buffer[48]; size_t n; /* Duplicated data */ static const struct test_loading_data data_duplicates[] = { { .n = "val/1", .v = "dup abc" }, { .n = "val/2", .v = "dup 123" }, { .n = "val/3", .v = "dup 11" }, { .n = "val/4", .v = "dup 34" }, { .n = "val/1", .v = "dup 56" }, { .n = "val/2", .v = "dup 7890" }, { .n = "val/4", .v = "dup niety" }, { .n = "val/3", .v = "dup er" }, { .n = "val/3", .v = "dup super" }, { .n = "val/3", .v = "dup xxx" }, { .n = NULL } }; settings_subsys_init(); /* Data that is going to be deleted */ strcpy(buffer, prefix); strcat(buffer, "/to_delete"); settings_save_one(buffer, "1", 2); (void) settings_delete(buffer); /* Saving all the data */ for (ldata = data_duplicates; ldata->n; ++ldata) { strcpy(buffer, prefix); strcat(buffer, "/"); strcat(buffer, ldata->n); settings_save_one(buffer, ldata->v, strlen(ldata->v) + 1); } for (ldata = data_final; ldata->n; ++ldata) { strcpy(buffer, prefix); strcat(buffer, "/"); strcat(buffer, ldata->n); settings_save_one(buffer, ldata->v, strlen(ldata->v) + 1); } memset(data_final_called, 0, sizeof(data_final_called)); rc = settings_load_subtree_direct( prefix, direct_filtered_loader, (void *)0x3456); zassert_equal(0, rc); /* Check if all the data was called */ for (n = 0; data_final[n].n; ++n) { zassert_equal(1, data_final_called[n], "Unexpected number of calls (%u) of (%s) element", n, data_final[n].n); } rc = settings_register(&filtered_loader_settings); zassert_true(rc == 0); rc = settings_load_subtree(prefix); zassert_equal(0, rc); /* Check if all the data was called */ for (n = 0; data_final[n].n; ++n) { zassert_equal(2, data_final_called[n], "Unexpected number of calls (%u) of (%s) element", n, data_final[n].n); } settings_deregister(&filtered_loader_settings); } #if defined(CONFIG_SETTINGS_SAVE_SINGLE_SUBTREE_WITHOUT_MODIFICATION) struct save_single_data { uint8_t first_val; uint8_t second_val; uint8_t third_val; uint8_t forth_val; bool first_second_export_called; bool first_second_commit_called; bool first_get_called; bool first_set_called; bool second_get_called; bool second_set_called; bool third_export_called; bool third_commit_called; bool third_get_called; bool third_set_called; bool forth_export_called; bool forth_commit_called; bool forth_get_called; bool forth_set_called; }; struct save_single_data single_data; int first_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { const char *next; if (settings_name_steq(name, "value1", &next) && !next) { if (len != sizeof(single_data.first_val)) { return -EINVAL; } (void)read_cb(cb_arg, &single_data.first_val, sizeof(single_data.first_val)); single_data.first_set_called = true; return 0; } if (settings_name_steq(name, "value2", &next) && !next) { if (len != sizeof(single_data.second_val)) { return -EINVAL; } (void)read_cb(cb_arg, &single_data.second_val, sizeof(single_data.second_val)); single_data.second_set_called = true; return 0; } return -ENOENT; } int first_get(const char *name, char *val, int val_len_max) { const char *next; if (val_len_max < 0) { return -EINVAL; } if (settings_name_steq(name, "value1", &next) && !next) { val_len_max = MIN(val_len_max, sizeof(single_data.first_val)); memcpy(val, &single_data.first_val, val_len_max); single_data.first_get_called = true; return val_len_max; } else if (settings_name_steq(name, "value2", &next) && !next) { val_len_max = MIN(val_len_max, sizeof(single_data.second_val)); memcpy(val, &single_data.second_val, val_len_max); single_data.second_get_called = true; return val_len_max; } return -ENOENT; } int first_commit(void) { single_data.first_second_commit_called = true; return 0; } int first_export(int (*cb)(const char *name, const void *value, size_t val_len)) { (void)cb("first/value1", &single_data.first_val, sizeof(single_data.first_val)); (void)cb("first/value2", &single_data.second_val, sizeof(single_data.second_val)); single_data.first_second_export_called = true; return 0; } static struct settings_handler first_settings = { .name = "first", .h_set = first_set, .h_get = first_get, .h_commit = first_commit, .h_export = first_export, }; int third_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { const char *next; if (settings_name_steq(name, "value3", &next) && !next) { if (len != sizeof(single_data.third_val)) { return -EINVAL; } (void)read_cb(cb_arg, &single_data.third_val, sizeof(single_data.third_val)); single_data.third_set_called = true; return 0; } return -ENOENT; } int third_get(const char *name, char *val, int val_len_max) { const char *next; if (val_len_max < 0) { return -EINVAL; } if (settings_name_steq(name, "value3", &next) && !next) { val_len_max = MIN(val_len_max, sizeof(single_data.third_val)); memcpy(val, &single_data.third_val, val_len_max); single_data.third_get_called = true; return val_len_max; } return -ENOENT; } int third_commit(void) { single_data.third_commit_called = true; return 0; } int third_export(int (*cb)(const char *name, const void *value, size_t val_len)) { (void)cb("first/other/value3", &single_data.third_val, sizeof(single_data.third_val)); single_data.third_export_called = true; return 0; } static struct settings_handler third_settings = { .name = "first/other", .h_set = third_set, .h_get = third_get, .h_commit = third_commit, .h_export = third_export, }; int forth_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { const char *next; if (settings_name_steq(name, "value4", &next) && !next) { if (len != sizeof(single_data.forth_val)) { return -EINVAL; } (void)read_cb(cb_arg, &single_data.forth_val, sizeof(single_data.forth_val)); single_data.forth_set_called = true; return 0; } return -ENOENT; } int forth_commit(void) { single_data.forth_commit_called = true; return 0; } int forth_export(int (*cb)(const char *name, const void *value, size_t val_len)) { (void)cb("first/expected_fail/value4", &single_data.forth_val, sizeof(single_data.forth_val)); single_data.forth_export_called = true; return 0; } static struct settings_handler forth_settings = { .name = "first/expected_fail", .h_set = forth_set, .h_get = NULL, .h_commit = forth_commit, .h_export = forth_export, }; static void single_modification_reset(void) { single_data.first_second_export_called = false; single_data.first_second_commit_called = false; single_data.first_get_called = false; single_data.first_set_called = false; single_data.second_get_called = false; single_data.second_set_called = false; single_data.third_export_called = false; single_data.third_commit_called = false; single_data.third_get_called = false; single_data.third_set_called = false; single_data.forth_export_called = false; single_data.forth_commit_called = false; single_data.forth_get_called = false; single_data.forth_set_called = false; } #endif ZTEST(settings_functional, test_single_save) { Z_TEST_SKIP_IFNDEF(CONFIG_SETTINGS_SAVE_SINGLE_SUBTREE_WITHOUT_MODIFICATION); #if defined(CONFIG_SETTINGS_SAVE_SINGLE_SUBTREE_WITHOUT_MODIFICATION) int rc; uint8_t dummy_value = 0xff; settings_subsys_init(); rc = settings_register(&first_settings); zassert_true(rc == 0); rc = settings_register(&third_settings); zassert_true(rc == 0); rc = settings_register(&forth_settings); zassert_true(rc == 0); settings_save_one("first/value1", &dummy_value, sizeof(dummy_value)); settings_save_one("first/value2", &dummy_value, sizeof(dummy_value)); settings_save_one("first/other/value3", &dummy_value, sizeof(dummy_value)); settings_save_one("first/expected_fail/value4", &dummy_value, sizeof(dummy_value)); rc = settings_load(); zassert_true(rc == 0); /* Test that options return errors with invalid parameters */ single_modification_reset(); single_data.first_val = 0x08; single_data.second_val = 0x09; single_data.third_val = 0x0a; single_data.forth_val = 0x0b; rc = settings_save_subtree_or_single_without_modification("first/value1", true, false); zassert_true(rc == -EPERM); zassert_equal(single_data.first_val, 0x08); zassert_equal(single_data.second_val, 0x09); zassert_equal(single_data.third_val, 0x0a); zassert_equal(single_data.forth_val, 0x0b); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_false(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); rc = settings_save_subtree_or_single_without_modification("first", false, true); zassert_true(rc == -EPERM); zassert_equal(single_data.first_val, 0x08); zassert_equal(single_data.second_val, 0x09); zassert_equal(single_data.third_val, 0x0a); zassert_equal(single_data.forth_val, 0x0b); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_false(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); rc = settings_save_subtree_or_single_without_modification("first/other/value1", true, false); zassert_true(rc == -EPERM); zassert_equal(single_data.first_val, 0x08); zassert_equal(single_data.second_val, 0x09); zassert_equal(single_data.third_val, 0x0a); zassert_equal(single_data.forth_val, 0x0b); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_false(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); rc = settings_save_subtree_or_single_without_modification("first/other", false, true); zassert_true(rc == -EPERM); zassert_equal(single_data.first_val, 0x08); zassert_equal(single_data.second_val, 0x09); zassert_equal(single_data.third_val, 0x0a); zassert_equal(single_data.forth_val, 0x0b); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_false(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); /* Test that it saves single values */ rc = settings_load(); zassert_true(rc == 0); single_modification_reset(); single_data.first_val = 0x01; rc = settings_save_subtree_or_single_without_modification("first/value1", false, true); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x01); zassert_equal(single_data.second_val, 0xff); zassert_equal(single_data.third_val, 0xff); zassert_equal(single_data.forth_val, 0xff); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_true(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_false(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); single_modification_reset(); single_data.first_val = 0x02; rc = settings_load_subtree("first"); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x01); zassert_equal(single_data.second_val, 0xff); zassert_equal(single_data.third_val, 0xff); zassert_equal(single_data.forth_val, 0xff); zassert_false(single_data.first_second_export_called); zassert_false(single_data.third_export_called); zassert_false(single_data.forth_export_called); zassert_true(single_data.first_second_commit_called); zassert_true(single_data.third_commit_called); zassert_true(single_data.forth_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.second_get_called); zassert_false(single_data.third_get_called); zassert_false(single_data.forth_get_called); zassert_true(single_data.first_set_called); zassert_true(single_data.second_set_called); zassert_true(single_data.third_set_called); zassert_true(single_data.forth_set_called); single_modification_reset(); single_data.first_val = 0x02; rc = settings_save_subtree_or_single_without_modification("first/value1", false, true); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x02); zassert_equal(single_data.second_val, 0xff); zassert_equal(single_data.third_val, 0xff); zassert_equal(single_data.forth_val, 0xff); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_true(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_false(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); single_modification_reset(); single_data.first_val = 0x03; rc = settings_load_subtree("first"); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x02); zassert_equal(single_data.second_val, 0xff); zassert_equal(single_data.third_val, 0xff); zassert_equal(single_data.forth_val, 0xff); zassert_false(single_data.first_second_export_called); zassert_false(single_data.third_export_called); zassert_false(single_data.forth_export_called); zassert_true(single_data.first_second_commit_called); zassert_true(single_data.third_commit_called); zassert_true(single_data.forth_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.second_get_called); zassert_false(single_data.third_get_called); zassert_false(single_data.forth_get_called); zassert_true(single_data.first_set_called); zassert_true(single_data.second_set_called); zassert_true(single_data.third_set_called); zassert_true(single_data.forth_set_called); /* Check changing second value and doing one write */ single_modification_reset(); single_data.first_val = 0x01; single_data.second_val = 0x20; rc = settings_save_subtree_or_single_without_modification("first/value2", false, true); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x01); zassert_equal(single_data.second_val, 0x20); zassert_equal(single_data.third_val, 0xff); zassert_equal(single_data.forth_val, 0xff); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_true(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_false(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); single_modification_reset(); single_data.first_val = 0x00; single_data.second_val = 0x00; rc = settings_load_subtree("first"); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x02); zassert_equal(single_data.second_val, 0x20); zassert_equal(single_data.third_val, 0xff); zassert_equal(single_data.forth_val, 0xff); zassert_false(single_data.first_second_export_called); zassert_false(single_data.third_export_called); zassert_false(single_data.forth_export_called); zassert_true(single_data.first_second_commit_called); zassert_true(single_data.third_commit_called); zassert_true(single_data.forth_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.second_get_called); zassert_false(single_data.third_get_called); zassert_false(single_data.forth_get_called); zassert_true(single_data.first_set_called); zassert_true(single_data.second_set_called); zassert_true(single_data.third_set_called); zassert_true(single_data.forth_set_called); /* Check doing a subtree update */ single_modification_reset(); single_data.first_val = 0x01; single_data.second_val = 0x20; single_data.third_val = 0x21; single_data.forth_val = 0x22; rc = settings_save_subtree_or_single_without_modification("first", true, false); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x01); zassert_equal(single_data.second_val, 0x20); zassert_equal(single_data.third_val, 0x21); zassert_equal(single_data.forth_val, 0x22); zassert_true(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_true(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_true(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); single_modification_reset(); single_data.first_val = 0x00; single_data.second_val = 0x00; single_data.third_val = 0x00; single_data.forth_val = 0x00; rc = settings_load_subtree("first"); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x01); zassert_equal(single_data.second_val, 0x20); zassert_equal(single_data.third_val, 0x21); zassert_equal(single_data.forth_val, 0x22); zassert_false(single_data.first_second_export_called); zassert_false(single_data.third_export_called); zassert_false(single_data.forth_export_called); zassert_true(single_data.first_second_commit_called); zassert_true(single_data.third_commit_called); zassert_true(single_data.forth_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.second_get_called); zassert_false(single_data.third_get_called); zassert_false(single_data.forth_get_called); zassert_true(single_data.first_set_called); zassert_true(single_data.second_set_called); zassert_true(single_data.third_set_called); zassert_true(single_data.forth_set_called); /* Check doing a limited subtree update */ single_modification_reset(); single_data.first_val = 0x41; single_data.second_val = 0x42; single_data.third_val = 0x43; single_data.forth_val = 0x44; rc = settings_save_subtree_or_single_without_modification("first/other", true, false); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x41); zassert_equal(single_data.second_val, 0x42); zassert_equal(single_data.third_val, 0x43); zassert_equal(single_data.forth_val, 0x44); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_true(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); single_modification_reset(); single_data.first_val = 0x00; single_data.second_val = 0x00; single_data.third_val = 0x00; single_data.forth_val = 0x00; rc = settings_load_subtree("first"); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x01); zassert_equal(single_data.second_val, 0x20); zassert_equal(single_data.third_val, 0x43); zassert_equal(single_data.forth_val, 0x22); zassert_false(single_data.first_second_export_called); zassert_false(single_data.third_export_called); zassert_false(single_data.forth_export_called); zassert_true(single_data.first_second_commit_called); zassert_true(single_data.third_commit_called); zassert_true(single_data.forth_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.second_get_called); zassert_false(single_data.third_get_called); zassert_false(single_data.forth_get_called); zassert_true(single_data.first_set_called); zassert_true(single_data.second_set_called); zassert_true(single_data.third_set_called); zassert_true(single_data.forth_set_called); /* Check that trying to save a single value without a get function does not work */ single_modification_reset(); single_data.first_val = 0x11; single_data.second_val = 0x22; single_data.third_val = 0x33; single_data.forth_val = 0x44; rc = settings_save_subtree_or_single_without_modification("first/expected_fail/value4", false, true); zassert_true(rc == -ENOSYS); zassert_equal(single_data.first_val, 0x11); zassert_equal(single_data.second_val, 0x22); zassert_equal(single_data.third_val, 0x33); zassert_equal(single_data.forth_val, 0x44); zassert_false(single_data.first_second_export_called); zassert_false(single_data.first_second_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.first_set_called); zassert_false(single_data.second_get_called); zassert_false(single_data.second_set_called); zassert_false(single_data.third_export_called); zassert_false(single_data.third_commit_called); zassert_false(single_data.third_get_called); zassert_false(single_data.third_set_called); zassert_false(single_data.forth_export_called); zassert_false(single_data.forth_commit_called); zassert_false(single_data.forth_get_called); zassert_false(single_data.forth_set_called); single_modification_reset(); rc = settings_load_subtree("first"); zassert_true(rc == 0); zassert_equal(single_data.first_val, 0x01); zassert_equal(single_data.second_val, 0x20); zassert_equal(single_data.third_val, 0x43); zassert_equal(single_data.forth_val, 0x22); zassert_false(single_data.first_second_export_called); zassert_false(single_data.third_export_called); zassert_false(single_data.forth_export_called); zassert_true(single_data.first_second_commit_called); zassert_true(single_data.third_commit_called); zassert_true(single_data.forth_commit_called); zassert_false(single_data.first_get_called); zassert_false(single_data.second_get_called); zassert_false(single_data.third_get_called); zassert_false(single_data.forth_get_called); zassert_true(single_data.first_set_called); zassert_true(single_data.second_set_called); zassert_true(single_data.third_set_called); zassert_true(single_data.forth_set_called); settings_deregister(&forth_settings); settings_deregister(&third_settings); settings_deregister(&first_settings); #endif }