diff --git a/doc/reference/storage/settings/settings.rst b/doc/reference/storage/settings/settings.rst index e5ee458ce9163..0a55648df7a2d 100644 --- a/doc/reference/storage/settings/settings.rst +++ b/doc/reference/storage/settings/settings.rst @@ -94,13 +94,16 @@ Loading data from persisted storage A call to ``settings_load()`` uses an ``h_set`` implementation to load settings data from storage to volatile memory. -For both FCB and filesystem back-end the most -recent key values are guaranteed by traversing all stored content -and (potentially) overwriting older key values with newer ones. After all data is loaded, the ``h_commit`` handler is issued, signalling the application that the settings were successfully retrieved. +Technically FCB and filesystem backends may store some history of the entities. +This means that the newest data entity is stored after any +older existing data entities. +Starting with Zephyr 2.1, the back-end must filter out all old entities and +call the callback with only the newest entity. + Storing data to persistent storage ********************************** diff --git a/include/settings/settings.h b/include/settings/settings.h index 2e9827dd56611..7d134cd9ed1b4 100644 --- a/include/settings/settings.h +++ b/include/settings/settings.h @@ -390,6 +390,12 @@ struct settings_store_itf { * Parameters: * - cs - Corresponding backend handler node, * - arg - Structure that holds additional data for data loading. + * + * @note + * Backend is expected not to provide duplicates of the entities. + * It means that if the backend does not contain any functionality to + * really delete old keys, it has to filter out old entities and call + * load callback only on the final entity. */ int (*csi_save_start)(struct settings_store *cs); diff --git a/subsys/settings/src/settings_fcb.c b/subsys/settings/src/settings_fcb.c index d47c6cfd59744..a33a25b85f5eb 100644 --- a/subsys/settings/src/settings_fcb.c +++ b/subsys/settings/src/settings_fcb.c @@ -6,8 +6,10 @@ */ #include +#include #include #include +#include #include "settings/settings.h" #include "settings/settings_fcb.h" @@ -18,11 +20,6 @@ LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL); #define SETTINGS_FCB_VERS 1 -struct settings_fcb_load_cb_arg { - line_load_cb cb; - void *cb_arg; -}; - static int settings_fcb_load(struct settings_store *cs, const struct settings_load_arg *arg); static int settings_fcb_save(struct settings_store *cs, const char *name, @@ -79,41 +76,88 @@ int settings_fcb_dst(struct settings_fcb *cf) return 0; } -static int settings_fcb_load_cb(struct fcb_entry_ctx *entry_ctx, void *arg) +/** + * @brief Check if there is any duplicate of the current setting + * + * This function checks if there is any duplicated data further in the buffer. + * + * @param cf FCB handler + * @param entry_ctx Current entry context + * @param name The name of the current entry + * + * @retval false No duplicates found + * @retval true Duplicate found + */ +static bool settings_fcb_check_duplicate(struct settings_fcb *cf, + const struct fcb_entry_ctx *entry_ctx, + const char * const name) { - struct settings_fcb_load_cb_arg *argp; - char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1]; - int rc; + struct fcb_entry_ctx entry2_ctx = *entry_ctx; - argp = (struct settings_fcb_load_cb_arg *)arg; + while (fcb_getnext(&cf->cf_fcb, &entry2_ctx.loc) == 0) { + char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1]; + size_t name2_len; - size_t len_read; + if (settings_line_name_read(name2, sizeof(name2), &name2_len, + &entry2_ctx)) { + LOG_ERR("failed to load line"); + continue; + } + name2[name2_len] = '\0'; + if (!strcmp(name, name2)) { + return true; + } + } + return false; +} - rc = settings_line_name_read(buf, sizeof(buf), &len_read, - (void *)&entry_ctx->loc); - if (rc) { +static int read_entry_len(const struct fcb_entry_ctx *entry_ctx, off_t off) +{ + if (off >= entry_ctx->loc.fe_data_len) { return 0; } - buf[len_read] = '\0'; - - /*name, val-read_cb-ctx, val-off*/ - /* take into account '=' separator after the name */ - argp->cb(buf, (void *)&entry_ctx->loc, len_read + 1, argp->cb_arg); - return 0; + return entry_ctx->loc.fe_data_len - off; } -static int settings_fcb_load_priv(struct settings_store *cs, line_load_cb cb, - void *cb_arg) +static int settings_fcb_load_priv(struct settings_store *cs, + line_load_cb cb, + void *cb_arg, + bool filter_duplicates) { struct settings_fcb *cf = (struct settings_fcb *)cs; - struct settings_fcb_load_cb_arg arg; + struct fcb_entry_ctx entry_ctx = { + {.fe_sector = NULL, .fe_elem_off = 0}, + .fap = cf->cf_fcb.fap + }; int rc; - arg.cb = cb; - arg.cb_arg = cb_arg; - rc = fcb_walk(&cf->cf_fcb, 0, settings_fcb_load_cb, &arg); - if (rc) { - return -EINVAL; + while ((rc = fcb_getnext(&cf->cf_fcb, &entry_ctx.loc)) == 0) { + char name[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1]; + size_t name_len; + int rc; + bool pass_entry = true; + + rc = settings_line_name_read(name, sizeof(name), &name_len, + (void *)&entry_ctx); + if (rc) { + LOG_ERR("Failed to load line name: %d", rc); + continue; + } + name[name_len] = '\0'; + + if (filter_duplicates && + (!read_entry_len(&entry_ctx, name_len+1) || + settings_fcb_check_duplicate(cf, &entry_ctx, name))) { + pass_entry = false; + } + /*name, val-read_cb-ctx, val-off*/ + /* take into account '=' separator after the name */ + if (pass_entry) { + cb(name, &entry_ctx, name_len + 1, cb_arg); + } + } + if (rc == -ENOTSUP) { + rc = 0; } return 0; } @@ -121,10 +165,13 @@ static int settings_fcb_load_priv(struct settings_store *cs, line_load_cb cb, static int settings_fcb_load(struct settings_store *cs, const struct settings_load_arg *arg) { - return settings_fcb_load_priv(cs, settings_line_load_cb, (void *)arg); + return settings_fcb_load_priv( + cs, + settings_line_load_cb, + (void *)arg, + true); } - static int read_handler(void *ctx, off_t off, char *buf, size_t *len) { struct fcb_entry_ctx *entry_ctx = ctx; @@ -307,7 +354,7 @@ static int settings_fcb_save(struct settings_store *cs, const char *name, cdca.val = (char *)value; cdca.is_dup = 0; cdca.val_len = val_len; - settings_fcb_load_priv(cs, settings_line_dup_check_cb, &cdca); + settings_fcb_load_priv(cs, settings_line_dup_check_cb, &cdca, false); if (cdca.is_dup == 1) { return 0; } diff --git a/subsys/settings/src/settings_file.c b/subsys/settings/src/settings_file.c index 07fef8bbd4ff0..dd2b0aac47f57 100644 --- a/subsys/settings/src/settings_file.c +++ b/subsys/settings/src/settings_file.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -14,6 +15,10 @@ #include "settings/settings_file.h" #include "settings_priv.h" +#include +LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL); + + static int settings_file_load(struct settings_store *cs, const struct settings_load_arg *arg); static int settings_file_save(struct settings_store *cs, const char *name, @@ -49,19 +54,63 @@ int settings_file_dst(struct settings_file *cf) return 0; } +/** + * @brief Check if there is any duplicate of the current setting + * + * This function checks if there is any duplicated data further in the buffer. + * + * @param cf FCB handler + * @param entry_ctx Current entry context + * @param name The name of the current entry + * + * @retval false No duplicates found + * @retval true Duplicate found + */ +bool settings_file_check_duplicate(struct settings_file *cf, + const struct line_entry_ctx *entry_ctx, + const char * const name) +{ + struct line_entry_ctx entry2_ctx = *entry_ctx; + + /* Searching the duplicates */ + while (settings_next_line_ctx(&entry2_ctx) == 0) { + char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1]; + size_t name2_len; + + if (entry2_ctx.len == 0) { + break; + } + + if (settings_line_name_read(name2, sizeof(name2), &name2_len, + &entry2_ctx)) { + continue; + } + name2[name2_len] = '\0'; + + if (!strcmp(name, name2)) { + return true; + } + } + return false; +} + +static int read_entry_len(const struct line_entry_ctx *entry_ctx, off_t off) +{ + if (off >= entry_ctx->len) { + return 0; + } + return entry_ctx->len - off; +} static int settings_file_load_priv(struct settings_store *cs, line_load_cb cb, - void *cb_arg) + void *cb_arg, bool filter_duplicates) { struct settings_file *cf = (struct settings_file *)cs; - char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1]; struct fs_dirent file_info; - struct fs_file_t file; - size_t len_read; + struct fs_file_t file; int lines; int rc; - struct line_entry_ctx entry_ctx = { .stor_ctx = (void *)&file, .seek = 0, @@ -81,23 +130,33 @@ static int settings_file_load_priv(struct settings_store *cs, line_load_cb cb, } while (1) { - rc = settings_next_line_ctx(&entry_ctx); + char name[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1]; + size_t name_len; + bool pass_entry = true; + rc = settings_next_line_ctx(&entry_ctx); if (rc || entry_ctx.len == 0) { break; } - rc = settings_line_name_read(buf, sizeof(buf), &len_read, - (void *)&entry_ctx); + rc = settings_line_name_read(name, sizeof(name), &name_len, + &entry_ctx); - if (rc || len_read == 0) { + if (rc || name_len == 0) { break; } - buf[len_read] = '\0'; + name[name_len] = '\0'; + if (filter_duplicates && + (!read_entry_len(&entry_ctx, name_len+1) || + settings_file_check_duplicate(cf, &entry_ctx, name))) { + pass_entry = false; + } /*name, val-read_cb-ctx, val-off*/ /* take into account '=' separator after the name */ - cb(buf, (void *)&entry_ctx, len_read + 1, cb_arg); + if (pass_entry) { + cb(name, (void *)&entry_ctx, name_len + 1, cb_arg); + } lines++; } @@ -113,7 +172,10 @@ static int settings_file_load_priv(struct settings_store *cs, line_load_cb cb, static int settings_file_load(struct settings_store *cs, const struct settings_load_arg *arg) { - return settings_file_load_priv(cs, settings_line_load_cb, (void *)arg); + return settings_file_load_priv(cs, + settings_line_load_cb, + (void *)arg, + true); } static void settings_tmpfile(char *dst, const char *src, char *pfx) @@ -359,7 +421,7 @@ static int settings_file_save(struct settings_store *cs, const char *name, cdca.val = (char *)value; cdca.is_dup = 0; cdca.val_len = val_len; - settings_file_load_priv(cs, settings_line_dup_check_cb, &cdca); + settings_file_load_priv(cs, settings_line_dup_check_cb, &cdca, false); if (cdca.is_dup == 1) { return 0; } diff --git a/tests/subsys/settings/functional/file/CMakeLists.txt b/tests/subsys/settings/functional/file/CMakeLists.txt new file mode 100644 index 0000000000000..76a21244a0a12 --- /dev/null +++ b/tests/subsys/settings/functional/file/CMakeLists.txt @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources ../src/*.c) +target_sources(app PRIVATE ${app_sources}) +zephyr_include_directories( + $ENV{ZEPHYR_BASE}/subsys/settings/include + $ENV{ZEPHYR_BASE}/subsys/settings/src + $ENV{ZEPHYR_BASE}/tests/subsys/settings/nffs/src + ) + +if(TEST) + target_compile_definitions(app PRIVATE + -DTEST_${TEST} + ) +endif() diff --git a/tests/subsys/settings/functional/file/native_posix.overlay b/tests/subsys/settings/functional/file/native_posix.overlay new file mode 100644 index 0000000000000..d936a341ab233 --- /dev/null +++ b/tests/subsys/settings/functional/file/native_posix.overlay @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &storage_partition; +/delete-node/ &scratch_partition; + +&flash0 { + /* + * For more information, see: + * http://docs.zephyrproject.org/latest/guides/dts/index.html#flash-partitions + */ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@70000 { + label = "storage"; + reg = <0x00070000 0x10000>; + }; + }; +}; diff --git a/tests/subsys/settings/functional/file/nrf52840_pca10056.overlay b/tests/subsys/settings/functional/file/nrf52840_pca10056.overlay new file mode 100644 index 0000000000000..90200eaedc7b4 --- /dev/null +++ b/tests/subsys/settings/functional/file/nrf52840_pca10056.overlay @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &storage_partition; +/delete-node/ &scratch_partition; + +&flash0 { + /* + * For more information, see: + * http://docs.zephyrproject.org/latest/guides/dts/index.html#flash-partitions + */ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@de000 { + label = "storage"; + reg = <0x000de000 0x00010000>; + }; + }; +}; diff --git a/tests/subsys/settings/functional/file/nrf52_pca10040.overlay b/tests/subsys/settings/functional/file/nrf52_pca10040.overlay new file mode 100644 index 0000000000000..b16c30e1ae455 --- /dev/null +++ b/tests/subsys/settings/functional/file/nrf52_pca10040.overlay @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &storage_partition; +/delete-node/ &scratch_partition; + +&flash0 { + /* + * For more information, see: + * http://docs.zephyrproject.org/latest/guides/dts/index.html#flash-partitions + */ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@70000 { + label = "storage"; + reg = <0x00070000 0x10000>; + }; + }; +}; diff --git a/tests/subsys/settings/functional/file/particle_xenon.overlay b/tests/subsys/settings/functional/file/particle_xenon.overlay new file mode 100644 index 0000000000000..96e337ef43e23 --- /dev/null +++ b/tests/subsys/settings/functional/file/particle_xenon.overlay @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Peter Bigot Consulting, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &storage_partition; + +&mx25l32 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + littlefs_partition: partition@0 { + label = "littlefs"; + reg = <0x00000000 0x00200000>; + }; + storage_partition: partition@200000 { + label = "storage"; + reg = <0x00200000 0x00004000>; + }; + partition@220000 { + label = "scratch2"; + reg = <0x00204000 0x001fc000>; + }; + }; +}; diff --git a/tests/subsys/settings/functional/file/prj.conf b/tests/subsys/settings/functional/file/prj.conf new file mode 100644 index 0000000000000..2cfae8427d7e8 --- /dev/null +++ b/tests/subsys/settings/functional/file/prj.conf @@ -0,0 +1,26 @@ +CONFIG_ARM_MPU=n + +CONFIG_ZTEST=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y + +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_NFFS=y +CONFIG_FS_NFFS_FLASH_DEV_NAME="flash_ctrl" +CONFIG_FS_NFFS_NUM_FILES=4 +CONFIG_FS_NFFS_NUM_DIRS=4 +CONFIG_FS_NFFS_NUM_INODES=1024 +CONFIG_FS_NFFS_NUM_BLOCKS=1024 +CONFIG_FS_NFFS_NUM_CACHE_INODES=1 +CONFIG_FS_NFFS_NUM_CACHE_BLOCKS=1 +CONFIG_NFFS_FILESYSTEM_MAX_AREAS=12 + +CONFIG_SETTINGS=y +CONFIG_SETTINGS_RUNTIME=y +CONFIG_SETTINGS_FS=y +CONFIG_SETTINGS_USE_BASE64=n + +CONFIG_SETTINGS_FS_DIR="/ff/settings" +CONFIG_SETTINGS_FS_FILE="/ff/settings/run" diff --git a/tests/subsys/settings/functional/file/prj_native_posix.conf b/tests/subsys/settings/functional/file/prj_native_posix.conf new file mode 100644 index 0000000000000..ea0736f5e38e7 --- /dev/null +++ b/tests/subsys/settings/functional/file/prj_native_posix.conf @@ -0,0 +1,24 @@ +CONFIG_ZTEST=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y + +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_NFFS=y +CONFIG_FS_NFFS_FLASH_DEV_NAME="flash_ctrl" +CONFIG_FS_NFFS_NUM_FILES=4 +CONFIG_FS_NFFS_NUM_DIRS=4 +CONFIG_FS_NFFS_NUM_INODES=1024 +CONFIG_FS_NFFS_NUM_BLOCKS=1024 +CONFIG_FS_NFFS_NUM_CACHE_INODES=1 +CONFIG_FS_NFFS_NUM_CACHE_BLOCKS=1 +CONFIG_NFFS_FILESYSTEM_MAX_AREAS=12 + +CONFIG_SETTINGS=y +CONFIG_SETTINGS_RUNTIME=y +CONFIG_SETTINGS_FS=y +CONFIG_SETTINGS_USE_BASE64=y + +CONFIG_SETTINGS_FS_DIR="/ff/settings" +CONFIG_SETTINGS_FS_FILE="/ff/settings/run" diff --git a/tests/subsys/settings/functional/file/prj_native_posix_64.conf b/tests/subsys/settings/functional/file/prj_native_posix_64.conf new file mode 100644 index 0000000000000..ee2f9e5e8c412 --- /dev/null +++ b/tests/subsys/settings/functional/file/prj_native_posix_64.conf @@ -0,0 +1,24 @@ +CONFIG_ZTEST=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y + +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_NFFS=y +CONFIG_FS_NFFS_FLASH_DEV_NAME="flash_ctrl" +CONFIG_FS_NFFS_NUM_FILES=4 +CONFIG_FS_NFFS_NUM_DIRS=4 +CONFIG_FS_NFFS_NUM_INODES=1024 +CONFIG_FS_NFFS_NUM_BLOCKS=1024 +CONFIG_FS_NFFS_NUM_CACHE_INODES=1 +CONFIG_FS_NFFS_NUM_CACHE_BLOCKS=1 +CONFIG_NFFS_FILESYSTEM_MAX_AREAS=12 + +CONFIG_SETTINGS=y +CONFIG_SETTINGS_RUNTIME=y +CONFIG_SETTINGS_FS=y +CONFIG_SETTINGS_USE_BASE64=n + +CONFIG_SETTINGS_FS_DIR="/ff/settings" +CONFIG_SETTINGS_FS_FILE="/ff/settings/run" diff --git a/tests/subsys/settings/functional/file/prj_qemu_x86.conf b/tests/subsys/settings/functional/file/prj_qemu_x86.conf new file mode 100644 index 0000000000000..ee2f9e5e8c412 --- /dev/null +++ b/tests/subsys/settings/functional/file/prj_qemu_x86.conf @@ -0,0 +1,24 @@ +CONFIG_ZTEST=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y + +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_NFFS=y +CONFIG_FS_NFFS_FLASH_DEV_NAME="flash_ctrl" +CONFIG_FS_NFFS_NUM_FILES=4 +CONFIG_FS_NFFS_NUM_DIRS=4 +CONFIG_FS_NFFS_NUM_INODES=1024 +CONFIG_FS_NFFS_NUM_BLOCKS=1024 +CONFIG_FS_NFFS_NUM_CACHE_INODES=1 +CONFIG_FS_NFFS_NUM_CACHE_BLOCKS=1 +CONFIG_NFFS_FILESYSTEM_MAX_AREAS=12 + +CONFIG_SETTINGS=y +CONFIG_SETTINGS_RUNTIME=y +CONFIG_SETTINGS_FS=y +CONFIG_SETTINGS_USE_BASE64=n + +CONFIG_SETTINGS_FS_DIR="/ff/settings" +CONFIG_SETTINGS_FS_FILE="/ff/settings/run" diff --git a/tests/subsys/settings/functional/file/testcase.yaml b/tests/subsys/settings/functional/file/testcase.yaml new file mode 100644 index 0000000000000..f5442c57638a2 --- /dev/null +++ b/tests/subsys/settings/functional/file/testcase.yaml @@ -0,0 +1,4 @@ +tests: + system.settings.file: + platform_whitelist: nrf52840_pca10056 nrf52_pca10040 native_posix native_posix_64 + tags: settings_file diff --git a/tests/subsys/settings/functional/src/settings_basic_test.c b/tests/subsys/settings/functional/src/settings_basic_test.c index b16e3693aa2de..badbf2d7a8ceb 100644 --- a/tests/subsys/settings/functional/src/settings_basic_test.c +++ b/tests/subsys/settings/functional/src/settings_basic_test.c @@ -19,22 +19,53 @@ LOG_MODULE_REGISTER(settings_basic_test); #if defined(CONFIG_SETTINGS_FCB) || defined(CONFIG_SETTINGS_NVS) #include #endif +#if IS_ENABLED(CONFIG_SETTINGS_FS) +#include +#include +#include +#endif /* The standard test expects a cleared flash area. Make sure it has * one. */ static void test_clear_settings(void) { - if (IS_ENABLED(CONFIG_SETTINGS_FCB)) { - const struct flash_area *fap; - int rc = flash_area_open(DT_FLASH_AREA_STORAGE_ID, &fap); +#if IS_ENABLED(CONFIG_SETTINGS_FCB) + const struct flash_area *fap; + int rc = flash_area_open(DT_FLASH_AREA_STORAGE_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"); + if (rc == 0) { + rc = flash_area_erase(fap, 0, fap->fa_size); + flash_area_close(fap); } + zassert_true(rc == 0, "clear settings failed"); +#endif +#if IS_ENABLED(CONFIG_SETTINGS_FS) + /* NFFS work area strcut */ + static struct nffs_flash_desc flash_desc; + + /* mounting info */ + static struct fs_mount_t nffs_mnt = { + .type = FS_NFFS, + .mnt_point = "/ff", + .fs_data = &flash_desc, + }; + struct device *flash_dev; + int rc; + + flash_dev = device_get_binding(CONFIG_FS_NFFS_FLASH_DEV_NAME); + zassert_not_null(flash_dev, "Can't bind to the flash device"); + + /* set backend storage dev */ + nffs_mnt.storage_dev = flash_dev; + + rc = fs_mount(&nffs_mnt); + zassert_true(rc == 0, "mounting nffs [%d]\n", rc); + + rc = fs_unlink(CONFIG_SETTINGS_FS_FILE); + zassert_true(rc == 0 || rc == -ENOENT, + "can't delete config file%d\n", rc); +#endif } /* @@ -438,13 +469,159 @@ static void test_direct_loading(void) zassert_equal(23, val_directly_loaded, NULL); } +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), NULL); + + rc = read_cb(cb_arg, buf, len); + zassert_equal(len, rc, NULL); + + 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, NULL); + return filtered_loader(key, len, read_cb, cb_arg); +} + + +static void test_direct_loading_filter(void) +{ + 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 } + }; + + /* Data that is going to be deleted */ + strcpy(buffer, prefix); + strcat(buffer, "/to_delete"); + settings_save_one(buffer, "1", 2); + 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, NULL); + + /* 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, NULL); + + rc = settings_load_subtree(prefix); + zassert_equal(0, rc, NULL); + + /* 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); + } +} + + void test_main(void) { ztest_test_suite(settings_test_suite, ztest_unit_test(test_clear_settings), ztest_unit_test(test_support_rtn), ztest_unit_test(test_register_and_loading), - ztest_unit_test(test_direct_loading) + ztest_unit_test(test_direct_loading), + ztest_unit_test(test_direct_loading_filter) ); ztest_run_test_suite(settings_test_suite);