zephyr/tests/subsys/settings/functional/src/settings_basic_test.c
Marcin Niestroj daee6cb4a9 settings: file: change FS (or file system) wording to File
Currently there is inconsistency in repository file names, APIs, Kconfig
options and documentation around file / file-system backend for settings
storage, as both "file" and "FS (file system)" are used. As an example,
there is `CONFIG_SETTINGS_FS` Kconfig option, but the file that implements
this settings backend is called `settings_file.c`. Another example are
names of static functions that implement settings storage API:
`settings_file_load()`, `settings_file_save()` and
`settings_fs_storage_get()`.

This backend is actually storing all settings in a single file, so it makes
sense to use "File" as the name of backend, instead of a more general
"FS" (which would make sense if several files would be used to store
settings).

Fix inconsistency in used wording in the tree and unify it to "settings
file backend". This naming is more precise to how the implementation looks.
It will also make it easier to grep through the codebase and analyze
existing code.

Deprecate settings_mount_fs_backend() function and all Kconfig options
starting with `CONFIG_SETTINGS_FS`.

Signed-off-by: Marcin Niestroj <m.niestroj@emb.dev>
2022-11-24 09:36:31 +01:00

621 lines
17 KiB
C

/*
* Copyright (c) 2018 Laczen
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief Settings functional test suite
*
*/
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#include <errno.h>
#include <zephyr/settings/settings.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(settings_basic_test);
#if defined(CONFIG_SETTINGS_FCB) || defined(CONFIG_SETTINGS_NVS)
#include <zephyr/storage/flash_map.h>
#if DT_HAS_CHOSEN(zephyr_settings_partition)
#define TEST_FLASH_AREA_ID DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_settings_partition))
#endif
#elif IS_ENABLED(CONFIG_SETTINGS_FILE)
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#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 !IS_ENABLED(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_erase(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;
rc = settings_subsys_init();
zassert_true(rc == 0, "subsys init failed");
settings_save_one("ps/ss/ss/val2", &val, sizeof(uint8_t));
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");
settings_save_one("ps/ss/val3", &val, sizeof(uint8_t));
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");
settings_save_one("ps/val1", &val, sizeof(uint8_t));
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");
/* 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, NULL);
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, NULL);
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);
}