diff --git a/include/settings/settings.h b/include/settings/settings.h new file mode 100644 index 00000000000..ffb33f63123 --- /dev/null +++ b/include/settings/settings.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SETTINGS_H_ +#define __SETTINGS_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup settings system + * @{ + */ + +#define SETTINGS_MAX_DIR_DEPTH 8 /* max depth of settings tree */ +#define SETTINGS_MAX_NAME_LEN (8 * SETTINGS_MAX_DIR_DEPTH) +#define SETTINGS_MAX_VAL_LEN 256 +#define SETTINGS_NAME_SEPARATOR "/" + +/* pleace for settings additions: + * up to 7 separators, '=', '\0' + */ +#define SETTINGS_EXTRA_LEN ((SETTINGS_MAX_DIR_DEPTH - 1) + 2) + +#define SETTINGS_NMGR_OP 0 + +/** + * Type of settings value. + */ +enum settings_type { + SETTINGS_NONE = 0, + SETTINGS_INT8, + SETTINGS_INT16, + SETTINGS_INT32, + SETTINGS_INT64, + SETTINGS_STRING, + SETTINGS_BYTES, + SETTINGS_FLOAT, + SETTINGS_DOUBLE, + SETTINGS_BOOL, +} __attribute__((__packed__)); + +/** + * Parameter to commit handler describing where data is going to. + */ +enum settings_export_tgt { + SETTINGS_EXPORT_PERSIST, /* Value is to be persisted. */ + SETTINGS_EXPORT_SHOW /* Value is to be displayed. */ +}; + +/** + * @struct settings_handler + * Config handlers for subtree implement a set of handler functions. + * These are registered using a call to settings_register(). + * + * @param settings_handler::node Linked list node info for module internal usage. + * + * @param settings_handler::name Name of subtree. + * + * @param settings_handler::h_get Get values handler of settings items + * identified by keyword names.Parameters: + * - argc - count of item in argv. + * - argv - array of pointers to keyword names. + * - val - buffer for a value. + * - val_len_max - size of that buffer. + * + * @param settings_handler::h_set Sey value handler of settings items + * identified by keyword names. Parameters: + * - argc - count of item in argv, argv - array of pointers to keyword names. + * - val- pointer to value to be set. + * + * @param settings_handler::h_commit This handler gets called after settings + * has been loaded in full. User might use it to apply setting to + * the application. + * + * @param settings_handler::h_export This gets called to dump all current + * settings items. + * This happens when settings_save() tries to save the settings. Parameters: + * - tgt: indicates where data is going to. + * - Export_function: the pointer to the internal function which appends + * a single key-value pair to persisted settings. Don't store duplicated + * value. The name is subtree/key string, val is the string with + * value. + * + * @remarks The User might limit a implementations of handler to serving only + * one keyword at one call - what will impose limit to get/set values using full + * subtree/key name. + */ +struct settings_handler { + sys_snode_t node; + char *name; + char *(*h_get)(int argc, char **argv, char *val, int val_len_max); + int (*h_set)(int argc, char **argv, char *val); + int (*h_commit)(void); + int (*h_export)(void (*export_func)(char *name, char *val), + enum settings_export_tgt tgt); +}; + +/** + * Initialization of settings and backend + * + * Can be called at application startup. + * In case the backend is NFFS Remember to call it after FS was mounted. + * For FCB backend it can be called without such a restriction. + */ +void settings_subsys_init(void); + +/** + * Register a handler for settings items. + * + * @param cf Structure containing registration info. + * + * @return 0 on success, non-zero on failure. + */ +int settings_register(struct settings_handler *cf); + +/** + * Load serialized items from registered persistence sources. Handlers for + * serialized item subtrees registered earlier will be called for encountered + * values. + * + * @return 0 on success, non-zero on failure. + */ +int settings_load(void); + +/** + * Save currently running serialized items. All serialized items which are different + * from currently persisted values will be saved. + * + * @return 0 on success, non-zero on failure. + */ +int settings_save(void); + +/** + * Write a single serialized value to persisted storage (if it has + * changed value). + * + * @param name Name/key of the settings item. + * @param var Value of the settings item. + * + * @return 0 on success, non-zero on failure. + */ +int settings_save_one(const char *name, char *var); + +/** + * Set settings item identified by @p name to be value @p val_str. + * This finds the settings handler for this subtree and calls it's + * set handler. + * + * @param name Name/key of the settings item. + * @param val_str Value of the settings item. + * + * @return 0 on success, non-zero on failure. + */ +int settings_set_value(char *name, char *val_str); + +/** + * Get value of settings item identified by @p name. + * This calls the settings handler h_get for the subtree. + * + * Configuration handler can copy the string to @p buf, the maximum + * number of bytes it will copy is limited by @p buf_len. + * + * @param name Name/key of the settings item. + * + * @param buf buffer for value of the settings item. + * If value is not string, the value will be filled in *buf. + * + * @param buf_len size of buf. + * + * @return value will be pointer to beginning of the buf, + * except for string it will pointer to beginning of string source. + */ +char *settings_get_value(char *name, char *buf, int buf_len); + +/** + * Call commit for all settings handler. This should apply all + * settings which has been set, but not applied yet. + * + * @param name Name of the settings subtree, or NULL to commit everything. + * + * @return 0 on success, non-zero on failure. + */ +int settings_commit(char *name); + +/** + * Convenience routine for converting value passed as a string to native + * data type. + * + * @param val_str Value of the settings item as string. + * @param type Type of the value to convert to. + * @param vp Pointer to variable to fill with the decoded value. + * @param maxlen the vp buffer size. + * + * @return 0 on success, non-zero on failure. + */ +int settings_value_from_str(char *val_str, enum settings_type type, void *vp, + int maxlen); + +/** + * Convenience routine for converting byte array passed as a base64 + * encoded string. + * + * @param val_str Value of the settings item as string. + * @param vp Pointer to variable to fill with the decoded value. + * @param len Size of that variable. On return the number of bytes in the array. + * + * @return 0 on success, non-zero on failure. + */ +int settings_bytes_from_str(char *val_str, void *vp, int *len); + +/** + * Convenience routine for converting native data type to a string. + * + * @param type Type of the value to convert from. + * @param vp Pointer to variable to convert. + * @param buf Buffer where string value will be stored. + * @param buf_len Size of the buffer. + * + * @return 0 on success, non-zero on failure. + */ +char *settings_str_from_value(enum settings_type type, void *vp, char *buf, + int buf_len); +#define SETTINGS_STR_FROM_BYTES_LEN(len) (((len) * 4 / 3) + 4) + +/** + * Convenience routine for converting byte array into a base64 + * encoded string. + * + * @param vp Pointer to variable to convert. + * @param vp_len Number of bytes to convert. + * @param buf Buffer where string value will be stored. + * @param buf_len Size of the buffer. + * + * @return 0 on success, non-zero on failure. + */ +char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len); + +#define SETTINGS_VALUE_SET(str, type, val) \ + settings_value_from_str((str), (type), &(val), sizeof(val)) + +/** + * @} settings + */ + +/* + * Config storage + */ +struct settings_store_itf; +struct settings_store { + sys_snode_t cs_next; + const struct settings_store_itf *cs_itf; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __SETTINGS_H_ */ diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index 4172db50fc8..11a45a017de 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -12,3 +12,4 @@ add_subdirectory_ifdef(CONFIG_NET_BUF net) add_subdirectory_ifdef(CONFIG_USB usb) add_subdirectory(random) add_subdirectory(storage) +add_subdirectory_ifdef(CONFIG_SETTINGS settings) diff --git a/subsys/Kconfig b/subsys/Kconfig index e0bf53771e6..9b6f01c0451 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -32,3 +32,5 @@ source "subsys/dfu/Kconfig" source "subsys/random/Kconfig" source "subsys/storage/Kconfig" + +source "subsys/settings/Kconfig" diff --git a/subsys/settings/CMakeLists.txt b/subsys/settings/CMakeLists.txt new file mode 100644 index 00000000000..2d4e4e5264d --- /dev/null +++ b/subsys/settings/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(src) +zephyr_include_directories( + include + ) diff --git a/subsys/settings/Kconfig b/subsys/settings/Kconfig new file mode 100644 index 00000000000..ef5878c6056 --- /dev/null +++ b/subsys/settings/Kconfig @@ -0,0 +1,87 @@ +# +# Copyright (c) 2018 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig SETTINGS + bool + default n + prompt "Enable settings subsystem with non-volatile storage" + depends on FILE_SYSTEM || FCB + select BASE64 + help + The settings subsystem allows its users to serialize and + deserialize state in memory into and from non-volatile memory. + It supports several back-ends to store and load serialized data from + and it can do so atomically for all involved modules. + +choice + prompt "Storage back-end" + default SETTINGS_FCB if FCB + depends on SETTINGS + help + Storage back-end to be used by the settings subsystem. + +config SETTINGS_FCB + bool "FCB" + depends on FCB + help + Use FCB as a settings storage back-end. + +config SETTINGS_FS + bool "File System" + depends on FILE_SYSTEM + help + Use a file system as a settings storage back-end. +endchoice + +config SETTINGS_FCB_NUM_AREAS + int + default 8 + depends on SETTINGS && SETTINGS_FCB + prompt "Number of flash areas used by the settings subsystem " + help + Number of areas to allocate in the settings FCB. A smaller number is + used if the flash hardware cannot support this value. + +config SETTINGS_FCB_MAGIC + hex + prompt "FCB magic for the settings subsystem" + default 0xc0ffeeee + depends on SETTINGS && SETTINGS_FCB + help + Magic 32-bit word for to identify valid settings area + +config SETTINGS_FCB_FLASH_AREA + int + prompt "Flash area id used for settings" + default 4 + depends on SETTINGS && SETTINGS_FCB + help + Id of the Flash area where FCB instance used for settings is + expected to operate. + +config SETTINGS_FS_DIR + string + prompt "Serialization directory" + default "/settings" + depends on SETTINGS && SETTINGS_FS + help + Directory where the settings data is stored + +config SETTINGS_FS_FILE + string + prompt "Default settings file" + default "/settings/run" + depends on SETTINGS && SETTINGS_FS + help + Full path to the default settings file. + +config SETTINGS_FS_MAX_LINES + int + prompt "Compression threshold" + default 32 + depends on SETTINGS && SETTINGS_FS + help + 'Limit how many items stored in a file before compressing' diff --git a/subsys/settings/include/settings/settings_fcb.h b/subsys/settings/include/settings/settings_fcb.h new file mode 100644 index 00000000000..ac7df1f9558 --- /dev/null +++ b/subsys/settings/include/settings/settings_fcb.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SETTINGS_FCB_H_ +#define __SETTINGS_FCB_H_ + +#include +#include "settings/settings.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct settings_fcb { + struct settings_store cf_store; + struct fcb cf_fcb; +}; + +extern int settings_fcb_src(struct settings_fcb *cf); +extern int settings_fcb_dst(struct settings_fcb *cf); + +#ifdef __cplusplus +} +#endif + +#endif /* __SETTINGS_FCB_H_ */ diff --git a/subsys/settings/include/settings/settings_file.h b/subsys/settings/include/settings/settings_file.h new file mode 100644 index 00000000000..882719ad9aa --- /dev/null +++ b/subsys/settings/include/settings/settings_file.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SETTINGS_FILE_H_ +#define __SETTINGS_FILE_H_ + +#include "settings/settings.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SETTINGS_FILE_NAME_MAX 32 /* max length for settings filename */ + +struct settings_file { + struct settings_store cf_store; + const char *cf_name; /* filename */ + int cf_maxlines; /* max # of lines before compressing */ + int cf_lines; /* private */ +}; + +/* register file to be source of settings */ +int settings_file_src(struct settings_file *cf); + +/* settings saves go to a file */ +int settings_file_dst(struct settings_file *cf); + +#ifdef __cplusplus +} +#endif + +#endif /* __SETTINGS_FILE_H_ */ diff --git a/subsys/settings/src/CMakeLists.txt b/subsys/settings/src/CMakeLists.txt new file mode 100644 index 00000000000..5ca59adb0fc --- /dev/null +++ b/subsys/settings/src/CMakeLists.txt @@ -0,0 +1,11 @@ +zephyr_include_directories($ENV{ZEPHYR_BASE}/ext/fs/nffs/include) + +zephyr_sources( + settings_store.c + settings.c + settings_init.c + settings_line.c + ) + +zephyr_sources_ifdef(CONFIG_SETTINGS_FS settings_file.c) +zephyr_sources_ifdef(CONFIG_SETTINGS_FCB settings_fcb.c) diff --git a/subsys/settings/src/settings.c b/subsys/settings/src/settings.c new file mode 100644 index 00000000000..51a1e1a9605 --- /dev/null +++ b/subsys/settings/src/settings.c @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include + +#include + +#include "base64.h" + +#include "settings/settings.h" +#include "settings_priv.h" +#include + +/* mbedtls-base64 lib encodes data to null-terminated string */ +#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1) + +sys_slist_t settings_handlers; + +static u8_t settings_cmd_inited; + +void settings_store_init(void); +static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base); +static s64_t dec_to_s64(char *p_str, char **e_ptr); + +void settings_init(void) +{ + if (!settings_cmd_inited) { + sys_slist_init(&settings_handlers); + settings_store_init(); + + settings_cmd_inited = 1; + } +} + +int settings_register(struct settings_handler *handler) +{ + sys_slist_prepend(&settings_handlers, &handler->node); + return 0; +} + +/* + * Find settings_handler based on name. + */ +struct settings_handler *settings_handler_lookup(char *name) +{ + struct settings_handler *ch; + + SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { + if (!strcmp(name, ch->name)) { + return ch; + } + } + return NULL; +} + +/* + * Separate string into argv array. + */ +int settings_parse_name(char *name, int *name_argc, char *name_argv[]) +{ + int i = 0; + + while (name) { + name_argv[i++] = name; + + while (1) { + if (*name == '\0') { + name = NULL; + break; + } + + if (*name == *SETTINGS_NAME_SEPARATOR) { + *name = '\0'; + name++; + break; + } + name++; + } + } + + *name_argc = i; + + return 0; +} + +static struct settings_handler *settings_parse_and_lookup(char *name, + int *name_argc, + char *name_argv[]) +{ + int rc; + + rc = settings_parse_name(name, name_argc, name_argv); + if (rc) { + return NULL; + } + return settings_handler_lookup(name_argv[0]); +} + +int settings_value_from_str(char *val_str, enum settings_type type, void *vp, + int maxlen) +{ + s32_t val; + s64_t val64; + char *eptr; + + if (!val_str) { + goto err; + } + switch (type) { + case SETTINGS_INT8: + case SETTINGS_INT16: + case SETTINGS_INT32: + case SETTINGS_BOOL: + val = strtol(val_str, &eptr, 0); + if (*eptr != '\0') { + goto err; + } + if (type == SETTINGS_BOOL) { + if (val < 0 || val > 1) { + goto err; + } + *(bool *)vp = val; + } else if (type == SETTINGS_INT8) { + if (val < INT8_MIN || val > UINT8_MAX) { + goto err; + } + *(int8_t *)vp = val; + } else if (type == SETTINGS_INT16) { + if (val < INT16_MIN || val > UINT16_MAX) { + goto err; + } + *(int16_t *)vp = val; + } else if (type == SETTINGS_INT32) { + *(s32_t *)vp = val; + } + break; + case SETTINGS_INT64: + val64 = dec_to_s64(val_str, &eptr); + if (*eptr != '\0') { + goto err; + } + *(s64_t *)vp = val64; + break; + case SETTINGS_STRING: + val = strlen(val_str); + if (val + 1 > maxlen) { + goto err; + } + strcpy(vp, val_str); + break; + default: + goto err; + } + return 0; +err: + return -EINVAL; +} + +int settings_bytes_from_str(char *val_str, void *vp, int *len) +{ + int err; + int rc; + + err = base64_decode(vp, *len, &rc, val_str, strlen(val_str)); + + if (err) { + return -1; + } + + *len = rc; + return 0; +} + +char *settings_str_from_value(enum settings_type type, void *vp, char *buf, + int buf_len) +{ + s32_t val; + + if (type == SETTINGS_STRING) { + return vp; + } + switch (type) { + case SETTINGS_INT8: + case SETTINGS_INT16: + case SETTINGS_INT32: + case SETTINGS_BOOL: + if (type == SETTINGS_BOOL) { + val = *(bool *)vp; + } else if (type == SETTINGS_INT8) { + val = *(int8_t *)vp; + } else if (type == SETTINGS_INT16) { + val = *(int16_t *)vp; + } else { + val = *(s32_t *)vp; + } + snprintf(buf, buf_len, "%ld", (long)val); + return buf; + case SETTINGS_INT64: + s64_to_dec(buf, buf_len, *(s64_t *)vp, 10); + return buf; + default: + return NULL; + } +} + +static void u64_to_dec(char *ptr, int buf_len, u64_t value, int base) +{ + u64_t t = 0, res = 0; + u64_t tmp = value; + int count = 0; + + if (ptr == NULL) { + return; + } + + if (tmp == 0) { + count++; + } + + while (tmp > 0) { + tmp = tmp/base; + count++; + } + + ptr += count; + + *ptr = '\0'; + + do { + res = value - base * (t = value / base); + if (res < 10) { + *--ptr = '0' + res; + } else if ((res >= 10) && (res < 16)) { + *--ptr = 'A' - 10 + res; + } + value = t; + } while (value != 0); +} + +static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base) +{ + u64_t val64; + + if (ptr == NULL || buf_len < 1) { + return; + } + + if (value < 0) { + *ptr = '-'; + ptr++; + buf_len--; + val64 = value * (-1); + } else { + val64 = value; + } + + u64_to_dec(ptr, buf_len, val64, base); +} + +static s64_t dec_to_s64(char *p_str, char **e_ptr) +{ + u64_t val = 0, prev_val = 0; + bool neg = false; + int digit; + + if (*p_str == '-') { + neg = true; + p_str++; + } else if (*p_str == '+') { + p_str++; + } + + while (1) { + if (*p_str >= '0' && *p_str <= '9') { + digit = *p_str - '0'; + } else { + break; + } + + val *= 10; + val += digit; + + /* this is only a fuse */ + if (val < prev_val) { + break; + } + + prev_val = val; + p_str++; + } + + if (e_ptr != 0) + *e_ptr = p_str; + + if (neg) { + return -val; + } else { + return val; + } +} + +char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len) +{ + if (BASE64_ENCODE_SIZE(vp_len) > buf_len) { + return NULL; + } + + size_t enc_len; + + base64_encode(buf, buf_len, &enc_len, vp, vp_len); + + return buf; +} + +int settings_set_value(char *name, char *val_str) +{ + int name_argc; + char *name_argv[SETTINGS_MAX_DIR_DEPTH]; + struct settings_handler *ch; + + ch = settings_parse_and_lookup(name, &name_argc, name_argv); + if (!ch) { + return -EINVAL; + } + + return ch->h_set(name_argc - 1, &name_argv[1], val_str); +} + +/* + * Get value in printable string form. If value is not string, the value + * will be filled in *buf. + * Return value will be pointer to beginning of that buffer, + * except for string it will pointer to beginning of string. + */ +char *settings_get_value(char *name, char *buf, int buf_len) +{ + int name_argc; + char *name_argv[SETTINGS_MAX_DIR_DEPTH]; + struct settings_handler *ch; + + ch = settings_parse_and_lookup(name, &name_argc, name_argv); + if (!ch) { + return NULL; + } + + if (!ch->h_get) { + return NULL; + } + return ch->h_get(name_argc - 1, &name_argv[1], buf, buf_len); +} + +int settings_commit(char *name) +{ + int name_argc; + char *name_argv[SETTINGS_MAX_DIR_DEPTH]; + struct settings_handler *ch; + int rc; + int rc2; + + if (name) { + ch = settings_parse_and_lookup(name, &name_argc, name_argv); + if (!ch) { + return -EINVAL; + } + if (ch->h_commit) { + return ch->h_commit(); + } else { + return 0; + } + } else { + rc = 0; + SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { + if (ch->h_commit) { + rc2 = ch->h_commit(); + if (!rc) { + rc = rc2; + } + } + } + return rc; + } +} diff --git a/subsys/settings/src/settings_fcb.c b/subsys/settings/src/settings_fcb.c new file mode 100644 index 00000000000..64b13188797 --- /dev/null +++ b/subsys/settings/src/settings_fcb.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "settings/settings.h" +#include "settings/settings_fcb.h" +#include "settings_priv.h" + +#define SETTINGS_FCB_VERS 1 + +struct settings_fcb_load_cb_arg { + load_cb cb; + void *cb_arg; +}; + +static int settings_fcb_load(struct settings_store *cs, load_cb cb, + void *cb_arg); +static int settings_fcb_save(struct settings_store *cs, const char *name, + const char *value); + +static struct settings_store_itf settings_fcb_itf = { + .csi_load = settings_fcb_load, + .csi_save = settings_fcb_save, +}; + +int settings_fcb_src(struct settings_fcb *cf) +{ + int rc; + + cf->cf_fcb.f_version = SETTINGS_FCB_VERS; + cf->cf_fcb.f_scratch_cnt = 1; + + while (1) { + rc = fcb_init(CONFIG_SETTINGS_FCB_FLASH_AREA, &cf->cf_fcb); + if (rc) { + return -EINVAL; + } + + /* + * Check if system was reset in middle of emptying a sector. + * This situation is recognized by checking if the scratch block + * is missing. + */ + if (fcb_free_sector_cnt(&cf->cf_fcb) < 1) { + + rc = flash_area_erase(cf->cf_fcb.fap, + cf->cf_fcb.f_active.fe_sector->fs_off, + cf->cf_fcb.f_active.fe_sector->fs_size); + + if (rc) { + return -EIO; + } + } else { + break; + } + } + + cf->cf_store.cs_itf = &settings_fcb_itf; + settings_src_register(&cf->cf_store); + + return 0; +} + +int settings_fcb_dst(struct settings_fcb *cf) +{ + cf->cf_store.cs_itf = &settings_fcb_itf; + settings_dst_register(&cf->cf_store); + + return 0; +} + +static int settings_fcb_load_cb(struct fcb_entry_ctx *entry_ctx, void *arg) +{ + struct settings_fcb_load_cb_arg *argp; + char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN + + SETTINGS_EXTRA_LEN]; + char *name_str; + char *val_str; + int rc; + int len; + + argp = (struct settings_fcb_load_cb_arg *)arg; + + len = entry_ctx->loc.fe_data_len; + if (len >= sizeof(buf)) { + len = sizeof(buf) - 1; + } + + rc = flash_area_read(entry_ctx->fap, + FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf, len); + + if (rc) { + return 0; + } + buf[len] = '\0'; + + rc = settings_line_parse(buf, &name_str, &val_str); + if (rc) { + return 0; + } + argp->cb(name_str, val_str, argp->cb_arg); + return 0; +} + +static int settings_fcb_load(struct settings_store *cs, load_cb cb, + void *cb_arg) +{ + struct settings_fcb *cf = (struct settings_fcb *)cs; + struct settings_fcb_load_cb_arg arg; + 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; + } + return 0; +} + +static int settings_fcb_var_read(struct fcb_entry_ctx *entry_ctx, char *buf, + char **name, char **val) +{ + int rc; + + rc = flash_area_read(entry_ctx->fap, + FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf, + entry_ctx->loc.fe_data_len); + if (rc) { + return rc; + } + buf[entry_ctx->loc.fe_data_len] = '\0'; + rc = settings_line_parse(buf, name, val); + return rc; +} + +static void settings_fcb_compress(struct settings_fcb *cf) +{ + int rc; + char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN + + SETTINGS_EXTRA_LEN]; + char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN + + SETTINGS_EXTRA_LEN]; + struct fcb_entry_ctx loc1; + struct fcb_entry_ctx loc2; + char *name1, *val1; + char *name2, *val2; + int copy; + + rc = fcb_append_to_scratch(&cf->cf_fcb); + if (rc) { + return; /* XXX */ + } + + loc1.fap = cf->cf_fcb.fap; + + loc1.loc.fe_sector = NULL; + loc1.loc.fe_elem_off = 0; + while (fcb_getnext(&cf->cf_fcb, &loc1.loc) == 0) { + if (loc1.loc.fe_sector != cf->cf_fcb.f_oldest) { + break; + } + rc = settings_fcb_var_read(&loc1, buf1, &name1, &val1); + if (rc) { + continue; + } + loc2 = loc1; + copy = 1; + while (fcb_getnext(&cf->cf_fcb, &loc2.loc) == 0) { + rc = settings_fcb_var_read(&loc2, buf2, &name2, &val2); + if (rc) { + continue; + } + if (!strcmp(name1, name2)) { + copy = 0; + break; + } + } + if (!copy) { + continue; + } + + /* + * Can't find one. Must copy. + */ + rc = flash_area_read(loc1.fap, FCB_ENTRY_FA_DATA_OFF(loc1.loc), + buf1, loc1.loc.fe_data_len); + if (rc) { + continue; + } + rc = fcb_append(&cf->cf_fcb, loc1.loc.fe_data_len, &loc2.loc); + if (rc) { + continue; + } + rc = flash_area_write(loc2.fap, FCB_ENTRY_FA_DATA_OFF(loc2.loc), + buf1, loc1.loc.fe_data_len); + if (rc) { + continue; + } + fcb_append_finish(&cf->cf_fcb, &loc2.loc); + } + rc = fcb_rotate(&cf->cf_fcb); + + __ASSERT(rc == 0, "Failed to fcb rotate.\n"); +} + +static int settings_fcb_append(struct settings_fcb *cf, char *buf, int len) +{ + int rc; + int i; + struct fcb_entry loc; + + for (i = 0; i < 10; i++) { + rc = fcb_append(&cf->cf_fcb, len, &loc); + if (rc != FCB_ERR_NOSPACE) { + break; + } + settings_fcb_compress(cf); + } + if (rc) { + return -EINVAL; + } + + rc = flash_area_write(cf->cf_fcb.fap, FCB_ENTRY_FA_DATA_OFF(loc), + buf, len); + if (rc) { + return -EINVAL; + } + fcb_append_finish(&cf->cf_fcb, &loc); + return 0; +} + +static int settings_fcb_save(struct settings_store *cs, const char *name, + const char *value) +{ + struct settings_fcb *cf = (struct settings_fcb *)cs; + char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN + + SETTINGS_EXTRA_LEN]; + int len; + + if (!name) { + return -EINVAL; + } + + len = settings_line_make(buf, sizeof(buf), name, value); + if (len < 0 || len + 2 > sizeof(buf)) { + return -EINVAL; + } + return settings_fcb_append(cf, buf, len); +} diff --git a/subsys/settings/src/settings_file.c b/subsys/settings/src/settings_file.c new file mode 100644 index 00000000000..687773b6e55 --- /dev/null +++ b/subsys/settings/src/settings_file.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +#include "settings/settings.h" +#include "settings/settings_file.h" +#include "settings_priv.h" + +static int settings_file_load(struct settings_store *cs, load_cb cb, + void *cb_arg); +static int settings_file_save(struct settings_store *cs, const char *name, + const char *value); + +static struct settings_store_itf settings_file_itf = { + .csi_load = settings_file_load, + .csi_save = settings_file_save, +}; + +/* + * Register a file to be a source of configuration. + */ +int settings_file_src(struct settings_file *cf) +{ + if (!cf->cf_name) { + return -EINVAL; + } + cf->cf_store.cs_itf = &settings_file_itf; + settings_src_register(&cf->cf_store); + + return 0; +} + +int settings_file_dst(struct settings_file *cf) +{ + if (!cf->cf_name) { + return -EINVAL; + } + cf->cf_store.cs_itf = &settings_file_itf; + settings_dst_register(&cf->cf_store); + + return 0; +} + +int settings_getnext_line(struct fs_file_t *file, char *buf, int blen, + off_t *loc) +{ + int rc; + char *end; + + rc = fs_seek(file, *loc, FS_SEEK_SET); + if (rc < 0) { + *loc = 0; + return -1; + } + + rc = fs_read(file, buf, blen); + if (rc <= 0) { + *loc = 0; + return -1; + } + + if (rc == blen) { + rc--; + } + buf[rc] = '\0'; + + end = strchr(buf, '\n'); + if (end) { + *end = '\0'; + } else { + end = strchr(buf, '\0'); + } + blen = end - buf; + *loc += (blen + 1); + return blen; +} + +/* + * Called to load configuration items. cb must be called for every configuration + * item found. + */ +static int settings_file_load(struct settings_store *cs, load_cb cb, + void *cb_arg) +{ + struct settings_file *cf = (struct settings_file *)cs; + struct fs_file_t file; + off_t loc; + char tmpbuf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN + + SETTINGS_EXTRA_LEN]; + char *name_str; + char *val_str; + int rc; + int lines; + + rc = fs_open(&file, cf->cf_name); + if (rc != 0) { + return -EINVAL; + } + + loc = 0; + lines = 0; + while (1) { + rc = settings_getnext_line(&file, tmpbuf, sizeof(tmpbuf), &loc); + if (loc == 0) { + break; + } + if (rc < 0) { + continue; + } + rc = settings_line_parse(tmpbuf, &name_str, &val_str); + if (rc != 0) { + continue; + } + lines++; + cb(name_str, val_str, cb_arg); + } + rc = fs_close(&file); + cf->cf_lines = lines; + + return rc; +} + +static void settings_tmpfile(char *dst, const char *src, char *pfx) +{ + int len; + int pfx_len; + + len = strlen(src); + pfx_len = strlen(pfx); + if (len + pfx_len >= SETTINGS_FILE_NAME_MAX) { + len = SETTINGS_FILE_NAME_MAX - pfx_len - 1; + } + memcpy(dst, src, len); + memcpy(dst + len, pfx, pfx_len); + dst[len + pfx_len] = '\0'; +} + +static int settings_file_create_or_replace(struct fs_file_t *zfp, + const char *file_name) +{ + struct fs_dirent entry; + + if (fs_stat(file_name, &entry) == 0) { + if (entry.type == FS_DIR_ENTRY_FILE) { + if (fs_unlink(file_name)) { + return -EIO; + } + } else { + return -EISDIR; + } + } + + return fs_open(zfp, file_name); +} +/* + * Try to compress configuration file by keeping unique names only. + */ +void settings_file_compress(struct settings_file *cf) +{ + int rc; + struct fs_file_t rf; + struct fs_file_t wf; + char tmp_file[SETTINGS_FILE_NAME_MAX]; + char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN + + SETTINGS_EXTRA_LEN]; + char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN + + SETTINGS_EXTRA_LEN]; + off_t loc1, loc2; + char *name1, *val1; + char *name2, *val2; + int copy; + int len, len2; + int lines; + + if (fs_open(&rf, cf->cf_name) != 0) { + return; + } + + settings_tmpfile(tmp_file, cf->cf_name, ".cmp"); + + if (settings_file_create_or_replace(&wf, tmp_file)) { + fs_close(&rf); + return; + } + + loc1 = 0; + lines = 0; + while (1) { + len = settings_getnext_line(&rf, buf1, sizeof(buf1), &loc1); + if (loc1 == 0 || len < 0) { + break; + } + rc = settings_line_parse(buf1, &name1, &val1); + if (rc) { + continue; + } + loc2 = loc1; + copy = 1; + while ((len2 = settings_getnext_line(&rf, buf2, sizeof(buf2), + &loc2)) > 0) { + rc = settings_line_parse(buf2, &name2, &val2); + if (rc) { + continue; + } + if (!strcmp(name1, name2)) { + copy = 0; + break; + } + } + if (!copy) { + continue; + } + + /* + * Can't find one. Must copy. + */ + len = settings_line_make(buf2, sizeof(buf2), name1, val1); + if (len < 0 || len + 2 > sizeof(buf2)) { + continue; + } + buf2[len++] = '\n'; + if (fs_write(&wf, buf2, len) != len) { + ARG_UNUSED(fs_close(&rf)); + ARG_UNUSED(fs_close(&wf)); + return; + } + lines++; + } + + len = fs_close(&wf); + len2 = fs_close(&rf); + if (len == 0 && len2 == 0 && fs_unlink(cf->cf_name) == 0) { + ARG_UNUSED(fs_rename(tmp_file, cf->cf_name)); + cf->cf_lines = lines; + /* + * XXX at settings_file_load(), look for .cmp if actual file does not + * exist. + */ + } +} + +/* + * Called to save configuration. + */ +static int settings_file_save(struct settings_store *cs, const char *name, + const char *value) +{ + struct settings_file *cf = (struct settings_file *)cs; + struct fs_file_t file; + char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN + + SETTINGS_EXTRA_LEN]; + int len; + int rc2; + int rc; + + if (!name) { + return -EINVAL; + } + + if (cf->cf_maxlines && (cf->cf_lines + 1 >= cf->cf_maxlines)) { + /* + * Compress before config file size exceeds + * the max number of lines. + */ + settings_file_compress(cf); + } + len = settings_line_make(buf, sizeof(buf), name, value); + if (len < 0 || len + 2 > sizeof(buf)) { + return -EINVAL; + } + buf[len++] = '\n'; + + /* + * Open the file to add this one value. + */ + rc = fs_open(&file, cf->cf_name); + if (rc == 0) { + rc = fs_seek(&file, 0, FS_SEEK_END); + if (rc == 0) { + rc2 = fs_write(&file, buf, len); + if (rc2 == len) { + cf->cf_lines++; + } + } + + rc2 = fs_close(&file); + if (rc == 0) { + rc = rc2; + } + } + + return rc; +} diff --git a/subsys/settings/src/settings_init.c b/subsys/settings/src/settings_init.c new file mode 100644 index 00000000000..ae9815a4be0 --- /dev/null +++ b/subsys/settings/src/settings_init.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#include + +#include "settings/settings.h" +#include "settings/settings_file.h" +#include + +void settings_init(void); + +#ifdef CONFIG_SETTINGS_FS +#include + +static struct settings_file config_init_settings_file = { + .cf_name = CONFIG_SETTINGS_FS_FILE, + .cf_maxlines = CONFIG_SETTINGS_FS_MAX_LINES +}; + +static void settings_init_fs(void) +{ + int rc; + + rc = settings_file_src(&config_init_settings_file); + if (rc) { + k_panic(); + } + + rc = settings_file_dst(&config_init_settings_file); + if (rc) { + k_panic(); + } +} + +#elif defined(CONFIG_SETTINGS_FCB) +#include "fcb.h" +#include "settings/settings_fcb.h" + +static struct flash_sector settings_fcb_area[CONFIG_SETTINGS_FCB_NUM_AREAS + 1]; + +static struct settings_fcb config_init_settings_fcb = { + .cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC, + .cf_fcb.f_sectors = settings_fcb_area, +}; + +static void settings_init_fcb(void) +{ + u32_t cnt = CONFIG_SETTINGS_FCB_NUM_AREAS + 1; + int rc; + const struct flash_area *fap; + + rc = flash_area_get_sectors(CONFIG_SETTINGS_FCB_FLASH_AREA, &cnt, + settings_fcb_area); + if (rc != 0 && rc != -ENOMEM) { + k_panic(); + } + + config_init_settings_fcb.cf_fcb.f_sector_cnt = cnt; + + rc = settings_fcb_src(&config_init_settings_fcb); + + if (rc != 0) { + k_panic(); + } + + rc = flash_area_open(CONFIG_SETTINGS_FCB_FLASH_AREA, &fap); + + if (rc == 0) { + rc = flash_area_erase(fap, 0, fap->fa_size); + flash_area_close(fap); + } + + if (rc != 0) { + k_panic(); + } else { + rc = settings_fcb_src(&config_init_settings_fcb); + } + + rc = settings_fcb_dst(&config_init_settings_fcb); + + if (rc != 0) { + k_panic(); + } +} + +#endif + +void settings_subsys_init(void) +{ + settings_init(); + +#ifdef CONFIG_SETTINGS_FS + settings_init_fs(); + + /* + * Must be called after root FS has been initialized. + */ + fs_mkdir(CONFIG_SETTINGS_FS_DIR); +#elif defined(CONFIG_SETTINGS_FCB) + settings_init_fcb(); +#endif +} diff --git a/subsys/settings/src/settings_line.c b/subsys/settings/src/settings_line.c new file mode 100644 index 00000000000..fb1f91a985c --- /dev/null +++ b/subsys/settings/src/settings_line.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "settings/settings.h" +#include "settings_priv.h" + +int settings_line_parse(char *buf, char **namep, char **valp) +{ + char *cp; + enum { + FIND_NAME, + FIND_NAME_END, + FIND_VAL, + FIND_VAL_END + } state = FIND_NAME; + + *valp = NULL; + for (cp = buf; *cp != '\0'; cp++) { + switch (state) { + case FIND_NAME: + if (!isspace((unsigned char)*cp)) { + *namep = cp; + state = FIND_NAME_END; + } + break; + case FIND_NAME_END: + if (*cp == '=') { + *cp = '\0'; + state = FIND_VAL; + } else if (isspace((unsigned char)*cp)) { + *cp = '\0'; + } + break; + case FIND_VAL: + if (!isspace((unsigned char)*cp)) { + *valp = cp; + state = FIND_VAL_END; + } + break; + case FIND_VAL_END: + if (isspace((unsigned char)*cp)) { + *cp = '\0'; + } + break; + } + } + + if (state == FIND_VAL_END || state == FIND_VAL) { + return 0; + } else { + return -1; + } +} + +int settings_line_make(char *dst, int dlen, const char *name, const char *value) +{ + int nlen; + int vlen; + int off; + + nlen = strlen(name); + + if (value) { + vlen = strlen(value); + } else { + vlen = 0; + } + + if (nlen + vlen + 2 > dlen) { + return -1; + } + + memcpy(dst, name, nlen); + off = nlen; + dst[off++] = '='; + + memcpy(dst + off, value, vlen); + off += vlen; + dst[off] = '\0'; + + return off; +} diff --git a/subsys/settings/src/settings_priv.h b/subsys/settings/src/settings_priv.h new file mode 100644 index 00000000000..1a2ebe7d027 --- /dev/null +++ b/subsys/settings/src/settings_priv.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SETTINGS_PRIV_H_ +#define __SETTINGS_PRIV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int settings_cli_register(void); +int settings_nmgr_register(void); + +struct mgmt_cbuf; +int settings_cbor_line(struct mgmt_cbuf *cb, char *name, int nlen, char *value, + int vlen); + +int settings_line_parse(char *buf, char **namep, char **valp); +int settings_line_make(char *dst, int dlen, const char *name, const char *val); +int settings_line_make2(char *dst, int dlen, const char *name, + const char *value); + +/* + * API for config storage. + */ +typedef void (*load_cb)(char *name, char *val, void *cb_arg); +struct settings_store_itf { + int (*csi_load)(struct settings_store *cs, load_cb cb, void *cb_arg); + int (*csi_save_start)(struct settings_store *cs); + int (*csi_save)(struct settings_store *cs, const char *name, + const char *value); + int (*csi_save_end)(struct settings_store *cs); +}; + +void settings_src_register(struct settings_store *cs); +void settings_dst_register(struct settings_store *cs); + +extern sys_slist_t settings_load_srcs; +extern sys_slist_t settings_handlers; +extern struct settings_store *settings_save_dst; + +#ifdef __cplusplus +} +#endif + +#endif /* __SETTINGS_PRIV_H_ */ diff --git a/subsys/settings/src/settings_store.c b/subsys/settings/src/settings_store.c new file mode 100644 index 00000000000..4c7f941c183 --- /dev/null +++ b/subsys/settings/src/settings_store.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include + +#include "settings/settings.h" +#include "settings_priv.h" + +struct settings_dup_check_arg { + const char *name; + const char *val; + int is_dup; +}; + +sys_slist_t settings_load_srcs; +struct settings_store *settings_save_dst; + +void settings_src_register(struct settings_store *cs) +{ + sys_snode_t *prev, *cur; + + prev = NULL; + + SYS_SLIST_FOR_EACH_NODE(&settings_load_srcs, cur) { + prev = cur; + } + + sys_slist_insert(&settings_load_srcs, prev, &cs->cs_next); +} + +void settings_dst_register(struct settings_store *cs) +{ + settings_save_dst = cs; +} + +static void settings_load_cb(char *name, char *val, void *cb_arg) +{ + settings_set_value(name, val); +} + +int settings_load(void) +{ + struct settings_store *cs; + + /* + * for every config store + * load config + * apply config + * commit all + */ + + SYS_SLIST_FOR_EACH_CONTAINER(&settings_load_srcs, cs, cs_next) { + cs->cs_itf->csi_load(cs, settings_load_cb, NULL); + } + return settings_commit(NULL); +} + +static void settings_dup_check_cb(char *name, char *val, void *cb_arg) +{ + struct settings_dup_check_arg *cdca = (struct settings_dup_check_arg *) + cb_arg; + + if (strcmp(name, cdca->name)) { + return; + } + if (!val) { + if (!cdca->val || cdca->val[0] == '\0') { + cdca->is_dup = 1; + } else { + cdca->is_dup = 0; + } + } else { + if (cdca->val && !strcmp(val, cdca->val)) { + cdca->is_dup = 1; + } else { + cdca->is_dup = 0; + } + } +} + +/* + * Append a single value to persisted config. Don't store duplicate value. + */ +int settings_save_one(const char *name, char *value) +{ + struct settings_store *cs; + struct settings_dup_check_arg cdca; + + cs = settings_save_dst; + if (!cs) { + return -ENOENT; + } + + /* + * Check if we're writing the same value again. + */ + cdca.name = name; + cdca.val = value; + cdca.is_dup = 0; + cs->cs_itf->csi_load(cs, settings_dup_check_cb, &cdca); + if (cdca.is_dup == 1) { + return 0; + } + return cs->cs_itf->csi_save(cs, name, value); +} + +/* + * Walk through all registered subsystems, and ask them to export their + * config variables. Persist these settings. + */ +static void settings_store_one(char *name, char *value) +{ + settings_save_one(name, value); +} + +int settings_save(void) +{ + struct settings_store *cs; + struct settings_handler *ch; + int rc; + int rc2; + + cs = settings_save_dst; + if (!cs) { + return -ENOENT; + } + + if (cs->cs_itf->csi_save_start) { + cs->cs_itf->csi_save_start(cs); + } + rc = 0; + + SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) { + if (ch->h_export) { + rc2 = ch->h_export(settings_store_one, + SETTINGS_EXPORT_PERSIST); + if (!rc) { + rc = rc2; + } + } + } + if (cs->cs_itf->csi_save_end) { + cs->cs_itf->csi_save_end(cs); + } + return rc; +} + +void settings_store_init(void) +{ + sys_slist_init(&settings_load_srcs); +} diff --git a/tests/subsys/settings/fcb/CMakeLists.txt b/tests/subsys/settings/fcb/CMakeLists.txt new file mode 100644 index 00000000000..f50fe70fd95 --- /dev/null +++ b/tests/subsys/settings/fcb/CMakeLists.txt @@ -0,0 +1,16 @@ +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c ../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/fcb/src + ) + +if(TEST) + target_compile_definitions(app PRIVATE + -DTEST_${TEST} + ) +endif() diff --git a/tests/subsys/settings/fcb/prj.conf b/tests/subsys/settings/fcb/prj.conf new file mode 100644 index 00000000000..8ea01bf6825 --- /dev/null +++ b/tests/subsys/settings/fcb/prj.conf @@ -0,0 +1,14 @@ +CONFIG_ZTEST=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_SOC_FLASH_NRF5=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_ARM_CORE_MPU=n +CONFIG_ARM_MPU=n +CONFIG_ARM_MPU_NRF52X=n +CONFIG_FCB=y + +CONFIG_SETTINGS=y +CONFIG_SETTINGS_FCB=y +CONFIG_SETTINGS_FCB_FLASH_AREA=3 diff --git a/tests/subsys/settings/fcb/src/settings_test.h b/tests/subsys/settings/fcb/src/settings_test.h new file mode 100644 index 00000000000..728fca353e6 --- /dev/null +++ b/tests/subsys/settings/fcb/src/settings_test.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _SETTINGS_TEST_FCB_H +#define _SETTINGS_TEST_FCB_H + +#include +#include +#include + +#include "settings/settings.h" +#include "flash_map.h" + +#ifdef __cplusplus +#extern "C" { +#endif + +#define SETTINGS_TEST_FCB_VAL_STR_CNT 64 +#define SETTINGS_TEST_FCB_FLASH_CNT 4 + +extern u8_t val8; +extern u32_t val32; +extern u64_t val64; + +extern int test_get_called; +extern int test_set_called; +extern int test_commit_called; +extern int test_export_block; + +extern int c2_var_count; + +extern struct flash_sector fcb_sectors[SETTINGS_TEST_FCB_FLASH_CNT]; + +extern char val_string[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN]; +extern char test_ref_value[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN]; + +extern struct settings_handler c_test_handlers[]; + +void ctest_clear_call_state(void); +int ctest_get_call_state(void); + +void config_wipe_srcs(void); +void config_wipe_fcb(struct flash_sector *fs, int cnt); + +void test_config_fill_area( + char test_value[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN], + int iteration); + +#ifdef __cplusplus +} +#endif +#endif /* _SETTINGS_TEST_FCB_H */ diff --git a/tests/subsys/settings/fcb/src/settings_test_compress_reset.c b/tests/subsys/settings/fcb/src/settings_test_compress_reset.c new file mode 100644 index 00000000000..5d3715563c2 --- /dev/null +++ b/tests/subsys/settings/fcb/src/settings_test_compress_reset.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_fcb.h" + +void test_config_compress_reset(void) +{ + int rc; + struct settings_fcb cf; + struct flash_sector *fa; + int elems[4]; + int i; + + config_wipe_srcs(); + config_wipe_fcb(fcb_sectors, ARRAY_SIZE(fcb_sectors)); + + cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC; + cf.cf_fcb.f_sectors = fcb_sectors; + cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors); + + rc = settings_fcb_src(&cf); + zassert_true(rc == 0, "can't register FCB as configuration source\n"); + + rc = settings_fcb_dst(&cf); + zassert_true(rc == 0, + "can't register FCB as configuration destination\n"); + + c2_var_count = 1; + memset(elems, 0, sizeof(elems)); + + for (i = 0; ; i++) { + test_config_fill_area(test_ref_value, i); + memcpy(val_string, test_ref_value, sizeof(val_string)); + + rc = settings_save(); + zassert_true(rc == 0, "fcb write error\n"); + + if (cf.cf_fcb.f_active.fe_sector == &fcb_sectors[2]) { + /* + * Started using space just before scratch. + */ + break; + } + memset(val_string, 0, sizeof(val_string)); + + rc = settings_load(); + zassert_true(rc == 0, "fcb read error\n"); + zassert_true(!memcmp(val_string, test_ref_value, + SETTINGS_MAX_VAL_LEN), + "bad value read\n"); + } + + fa = cf.cf_fcb.f_active.fe_sector; + rc = fcb_append_to_scratch(&cf.cf_fcb); + zassert_true(rc == 0, "fcb_append_to_scratch call failure\n"); + zassert_true(fcb_free_sector_cnt(&cf.cf_fcb) == 0, + "expected non of free sectors\n"); + zassert_true(fa != cf.cf_fcb.f_active.fe_sector, + "active page should change\n"); + + config_wipe_srcs(); + + memset(&cf, 0, sizeof(cf)); + + cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC; + cf.cf_fcb.f_sectors = fcb_sectors; + cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors); + + rc = settings_fcb_src(&cf); + zassert_true(rc == 0, "can't register FCB as configuration source\n"); + + rc = settings_fcb_dst(&cf); + zassert_true(rc == 0, + "can't register FCB as configuration destination\n"); + + + zassert_true(fcb_free_sector_cnt(&cf.cf_fcb) == 1, + "expected one free sector\n"); + zassert_true(fa == cf.cf_fcb.f_active.fe_sector, + "active sector should become free after garbage collection"); + + c2_var_count = 0; +} diff --git a/tests/subsys/settings/fcb/src/settings_test_empty_fcb.c b/tests/subsys/settings/fcb/src/settings_test_empty_fcb.c new file mode 100644 index 00000000000..0a94bd4db4c --- /dev/null +++ b/tests/subsys/settings/fcb/src/settings_test_empty_fcb.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_fcb.h" + +void test_config_empty_fcb(void) +{ + int rc; + struct settings_fcb cf; + + config_wipe_srcs(); + config_wipe_fcb(fcb_sectors, ARRAY_SIZE(fcb_sectors)); + + cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC; + cf.cf_fcb.f_sectors = fcb_sectors; + cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors); + + rc = settings_fcb_src(&cf); + zassert_true(rc == 0, "settings_fcb_src call should succeed\n"); + + /* + * No values + */ + settings_load(); + + config_wipe_srcs(); + ctest_clear_call_state(); +} diff --git a/tests/subsys/settings/fcb/src/settings_test_fcb.c b/tests/subsys/settings/fcb/src/settings_test_fcb.c new file mode 100644 index 00000000000..404e34cd0b9 --- /dev/null +++ b/tests/subsys/settings/fcb/src/settings_test_fcb.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "settings_test.h" +#include "settings_priv.h" +#include "flash_map.h" + +u8_t val8; +u32_t val32; +u64_t val64; + +int test_get_called; +int test_set_called; +int test_commit_called; +int test_export_block; + +int c2_var_count = 1; + +char *c1_handle_get(int argc, char **argv, char *val, int val_len_max); +int c1_handle_set(int argc, char **argv, char *val); +int c1_handle_commit(void); +int c1_handle_export(void (*cb)(char *name, char *value), + enum settings_export_tgt tgt); + +char *c2_handle_get(int argc, char **argv, char *val, int val_len_max); +int c2_handle_set(int argc, char **argv, char *val); +int c2_handle_export(void (*cb)(char *name, char *value), + enum settings_export_tgt tgt); + +char *c3_handle_get(int argc, char **argv, char *val, int val_len_max); +int c3_handle_set(int argc, char **argv, char *val); +int c3_handle_export(void (*cb)(char *name, char *value), + enum settings_export_tgt tgt); + +struct settings_handler c_test_handlers[] = { + { + .name = "myfoo", + .h_get = c1_handle_get, + .h_set = c1_handle_set, + .h_commit = c1_handle_commit, + .h_export = c1_handle_export + }, + { + .name = "2nd", + .h_get = c2_handle_get, + .h_set = c2_handle_set, + .h_commit = NULL, + .h_export = c2_handle_export + }, + { + .name = "3", + .h_get = c3_handle_get, + .h_set = c3_handle_set, + .h_commit = NULL, + .h_export = c3_handle_export + } +}; + +char val_string[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN]; +char test_ref_value[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN]; + +char *c1_handle_get(int argc, char **argv, char *val, int val_len_max) +{ + test_get_called = 1; + + if (argc == 1 && !strcmp(argv[0], "mybar")) { + return settings_str_from_value(SETTINGS_INT8, &val8, val, + val_len_max); + } + + if (argc == 1 && !strcmp(argv[0], "mybar64")) { + return settings_str_from_value(SETTINGS_INT64, &val64, val, + val_len_max); + } + + return NULL; +} + +int c1_handle_set(int argc, char **argv, char *val) +{ + u8_t newval; + u64_t newval64; + int rc; + + test_set_called = 1; + if (argc == 1 && !strcmp(argv[0], "mybar")) { + rc = SETTINGS_VALUE_SET(val, SETTINGS_INT8, newval); + zassert_true(rc == 0, "SETTINGS_VALUE_SET callback"); + val8 = newval; + return 0; + } + + if (argc == 1 && !strcmp(argv[0], "mybar64")) { + rc = SETTINGS_VALUE_SET(val, SETTINGS_INT64, newval64); + zassert_true(rc == 0, "SETTINGS_VALUE_SET callback"); + val64 = newval64; + return 0; + } + + return -ENOENT; +} + +int c1_handle_commit(void) +{ + test_commit_called = 1; + return 0; +} + +int c1_handle_export(void (*cb)(char *name, char *value), + enum settings_export_tgt tgt) +{ + char value[32]; + + if (test_export_block) { + return 0; + } + + settings_str_from_value(SETTINGS_INT8, &val8, value, sizeof(value)); + cb("myfoo/mybar", value); + + settings_str_from_value(SETTINGS_INT64, &val64, value, sizeof(value)); + cb("myfoo/mybar64", value); + + return 0; +} + +void ctest_clear_call_state(void) +{ + test_get_called = 0; + test_set_called = 0; + test_commit_called = 0; +} + +int ctest_get_call_state(void) +{ + return test_get_called + test_set_called + test_commit_called; +} + +void config_wipe_srcs(void) +{ + sys_slist_init(&settings_load_srcs); + settings_save_dst = NULL; +} + +struct flash_sector fcb_sectors[SETTINGS_TEST_FCB_FLASH_CNT] = { + [0] = { + .fs_off = 0x00000000, + .fs_size = 16 * 1024 + }, + [1] = { + .fs_off = 0x00004000, + .fs_size = 16 * 1024 + }, + [2] = { + .fs_off = 0x00008000, + .fs_size = 16 * 1024 + }, + [3] = { + .fs_off = 0x0000c000, + .fs_size = 16 * 1024 + } +}; + +void config_wipe_fcb(struct flash_sector *fs, int cnt) +{ + const struct flash_area *fap; + int rc; + int i; + + rc = flash_area_open(CONFIG_SETTINGS_FCB_FLASH_AREA, &fap); + + for (i = 0; i < cnt; i++) { + rc = flash_area_erase(fap, fs[i].fs_off, fs[i].fs_size); + zassert_true(rc == 0, "Can't get flash area\n"); + } +} + +void +test_config_fill_area(char test_value[SETTINGS_TEST_FCB_VAL_STR_CNT] + [SETTINGS_MAX_VAL_LEN], + int iteration) +{ + int i, j; + + for (j = 0; j < 64; j++) { + for (i = 0; i < SETTINGS_MAX_VAL_LEN; i++) { + test_value[j][i] = ((j * 2) + i + iteration) % 10 + '0'; + } + test_value[j][sizeof(test_value[j]) - 1] = '\0'; + } +} + +char *c2_var_find(char *name) +{ + int idx = 0; + int len; + char *eptr; + + len = strlen(name); + zassert_true(len > 6, "string type expected\n"); + zassert_true(!strncmp(name, "string", 6), "string type expected\n"); + + idx = strtoul(&name[6], &eptr, 10); + zassert_true(*eptr == '\0', "EOF\n"); + zassert_true(idx < c2_var_count, + "var index greather than any exporter\n"); + + return val_string[idx]; +} + +char *c2_handle_get(int argc, char **argv, char *val, int val_len_max) +{ + int len; + char *valptr; + + if (argc == 1) { + valptr = c2_var_find(argv[0]); + if (!valptr) { + return NULL; + } + + len = strlen(val_string[0]); + if (len > val_len_max) { + len = val_len_max; + } + + strncpy(val, valptr, len); + } + + return NULL; +} + +int c2_handle_set(int argc, char **argv, char *val) +{ + char *valptr; + + if (argc == 1) { + valptr = c2_var_find(argv[0]); + if (!valptr) { + return -ENOENT; + } + + if (val) { + strncpy(valptr, val, sizeof(val_string[0])); + } else { + memset(valptr, 0, sizeof(val_string[0])); + } + + return 0; + } + + return -ENOENT; +} + +int c2_handle_export(void (*cb)(char *name, char *value), + enum settings_export_tgt tgt) +{ + int i; + char name[32]; + + for (i = 0; i < c2_var_count; i++) { + snprintf(name, sizeof(name), "2nd/string%d", i); + cb(name, val_string[i]); + } + + return 0; +} + +char *c3_handle_get(int argc, char **argv, char *val, int val_len_max) +{ + if (argc == 1 && !strcmp(argv[0], "v")) { + return settings_str_from_value(SETTINGS_INT32, &val32, val, + val_len_max); + } + + return NULL; +} + +int c3_handle_set(int argc, char **argv, char *val) +{ + u32_t newval; + int rc; + + if (argc == 1 && !strcmp(argv[0], "v")) { + rc = SETTINGS_VALUE_SET(val, SETTINGS_INT32, newval); + zassert_true(rc == 0, "SETTINGS_VALUE_SET callback"); + val32 = newval; + return 0; + } + + return -ENOENT; +} + +int c3_handle_export(void (*cb)(char *name, char *value), + enum settings_export_tgt tgt) +{ + char value[32]; + + settings_str_from_value(SETTINGS_INT32, &val32, value, sizeof(value)); + cb("3/v", value); + + return 0; +} + +void config_empty_lookups(void); +void test_config_insert(void); +void test_config_getset_unknown(void); +void test_config_getset_int(void); +void test_config_getset_bytes(void); +void test_config_getset_int64(void); +void test_config_commit(void); + +void test_config_empty_fcb(void); +void test_config_save_1_fcb(void); +void test_config_insert2(void); +void test_config_save_2_fcb(void); +void test_config_insert3(void); +void test_config_save_3_fcb(void); +void test_config_compress_reset(void); +void test_config_save_one_fcb(void); + +void test_main(void *p1, void *p2, void *p3) +{ + ztest_test_suite(test_config_fcb, + /* Config tests */ + ztest_unit_test(config_empty_lookups), + ztest_unit_test(test_config_insert), + ztest_unit_test(test_config_getset_unknown), + ztest_unit_test(test_config_getset_int), + ztest_unit_test(test_config_getset_bytes), + ztest_unit_test(test_config_getset_int64), + ztest_unit_test(test_config_commit), + /* FCB as backing storage*/ + ztest_unit_test(test_config_empty_fcb), + ztest_unit_test(test_config_save_1_fcb), + ztest_unit_test(test_config_insert2), + ztest_unit_test(test_config_save_2_fcb), + ztest_unit_test(test_config_insert3), + ztest_unit_test(test_config_save_3_fcb), + ztest_unit_test(test_config_compress_reset), + ztest_unit_test(test_config_save_one_fcb) + ); + + ztest_run_test_suite(test_config_fcb); +} diff --git a/tests/subsys/settings/fcb/src/settings_test_save_1_fcb.c b/tests/subsys/settings/fcb/src/settings_test_save_1_fcb.c new file mode 100644 index 00000000000..f27fb3b3035 --- /dev/null +++ b/tests/subsys/settings/fcb/src/settings_test_save_1_fcb.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_fcb.h" + +void test_config_save_1_fcb(void) +{ + int rc; + struct settings_fcb cf; + + config_wipe_srcs(); + + cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC; + cf.cf_fcb.f_sectors = fcb_sectors; + cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors); + + rc = settings_fcb_src(&cf); + zassert_true(rc == 0, "can't register FCB as configuration source\n"); + + rc = settings_fcb_dst(&cf); + zassert_true(rc == 0, + "can't register FCB as configuration destination\n"); + + val8 = 33; + rc = settings_save(); + zassert_true(rc == 0, "fcb write error\n"); + + val8 = 0; + + rc = settings_load(); + zassert_true(rc == 0, "fcb redout error\n"); + zassert_true(val8 == 33, "bad value read\n"); +} diff --git a/tests/subsys/settings/fcb/src/settings_test_save_2_fcb.c b/tests/subsys/settings/fcb/src/settings_test_save_2_fcb.c new file mode 100644 index 00000000000..f54a78a4a10 --- /dev/null +++ b/tests/subsys/settings/fcb/src/settings_test_save_2_fcb.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_fcb.h" + +#ifdef TEST_LONG +#define TESTS_S2_FCB_ITERATIONS 32 +#else +#define TESTS_S2_FCB_ITERATIONS 2 +#endif + +void test_config_save_2_fcb(void) +{ + int rc; + struct settings_fcb cf; + + int i; + + config_wipe_srcs(); + + cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC; + cf.cf_fcb.f_sectors = fcb_sectors; + cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors); + + rc = settings_fcb_src(&cf); + zassert_true(rc == 0, "can't register FCB as configuration source\n"); + + rc = settings_fcb_dst(&cf); + zassert_true(rc == 0, + "can't register FCB as configuration destination\n"); + + test_config_fill_area(test_ref_value, 0); + memcpy(val_string, test_ref_value, sizeof(val_string)); + + val8 = 42; + rc = settings_save(); + zassert_true(rc == 0, "fcb write error\n"); + + val8 = 0; + memset(val_string[0], 0, sizeof(val_string[0])); + rc = settings_load(); + zassert_true(rc == 0, "fcb read error\n"); + zassert_true(val8 == 42, "bad value read\n"); + zassert_true(!strcmp(val_string[0], test_ref_value[0]), + "bad value read\n"); + test_export_block = 1; + + /* + * Now add the number of settings to max. Keep adjusting the test_data, + * check that rollover happens when it's supposed to. + */ + c2_var_count = 64; + + for (i = 0; i < TESTS_S2_FCB_ITERATIONS; i++) { + test_config_fill_area(test_ref_value, i); + memcpy(val_string, test_ref_value, sizeof(val_string)); + + rc = settings_save(); + zassert_true(rc == 0, "fcb write error\n"); + + memset(val_string, 0, sizeof(val_string)); + + val8 = 0; + rc = settings_load(); + zassert_true(rc == 0, "fcb read error\n"); + zassert_true(!memcmp(val_string, test_ref_value, + sizeof(val_string)), + "bad value read\n"); + zassert_true(val8 == 42, "bad value read\n"); + } + c2_var_count = 0; +} diff --git a/tests/subsys/settings/fcb/src/settings_test_save_3_fcb.c b/tests/subsys/settings/fcb/src/settings_test_save_3_fcb.c new file mode 100644 index 00000000000..1c515a76b7b --- /dev/null +++ b/tests/subsys/settings/fcb/src/settings_test_save_3_fcb.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_fcb.h" + +#ifdef TEST_LONG +#define TESTS_S3_FCB_ITERATIONS 4096 +#else +#define TESTS_S3_FCB_ITERATIONS 100 +#endif + +void test_config_save_3_fcb(void) +{ + int rc; + struct settings_fcb cf; + int i; + + config_wipe_srcs(); + config_wipe_fcb(fcb_sectors, ARRAY_SIZE(fcb_sectors)); + + cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC; + cf.cf_fcb.f_sectors = fcb_sectors; + cf.cf_fcb.f_sector_cnt = 4; + + rc = settings_fcb_src(&cf); + zassert_true(rc == 0, "can't register FCB as configuration source\n"); + + rc = settings_fcb_dst(&cf); + zassert_true(rc == 0, + "can't register FCB as configuration destination\n"); + + for (i = 0; i < TESTS_S3_FCB_ITERATIONS; i++) { + val32 = i; + + rc = settings_save(); + zassert_true(rc == 0, "fcb write error\n"); + + val32 = 0; + + rc = settings_load(); + zassert_true(rc == 0, "fcb read error\n"); + zassert_true(val32 == i, "bad value read\n"); + } +} diff --git a/tests/subsys/settings/fcb/src/settings_test_save_one_fcb.c b/tests/subsys/settings/fcb/src/settings_test_save_one_fcb.c new file mode 100644 index 00000000000..939f9c32497 --- /dev/null +++ b/tests/subsys/settings/fcb/src/settings_test_save_one_fcb.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_fcb.h" + +void test_config_save_one_fcb(void) +{ + int rc; + struct settings_fcb cf; + + config_wipe_srcs(); + config_wipe_fcb(fcb_sectors, ARRAY_SIZE(fcb_sectors)); + + cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC; + cf.cf_fcb.f_sectors = fcb_sectors; + cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors); + + rc = settings_fcb_src(&cf); + zassert_true(rc == 0, "can't register FCB as configuration source\n"); + + rc = settings_fcb_dst(&cf); + zassert_true(rc == 0, + "can't register FCB as configuration destination\n"); + + val8 = 33; + rc = settings_save(); + zassert_true(rc == 0, "fcb write error\n"); + + rc = settings_save_one("myfoo/mybar", "42"); + zassert_true(rc == 0, "fcb one item write error\n"); + + rc = settings_load(); + zassert_true(rc == 0, "fcb read error\n"); + zassert_true(val8 == 42, "bad value read\n"); + + rc = settings_save_one("myfoo/mybar", "44"); + zassert_true(rc == 0, "fcb one item write error\n"); + + rc = settings_load(); + zassert_true(rc == 0, "fcb read error\n"); + zassert_true(val8 == 44, "bad value read\n"); +} diff --git a/tests/subsys/settings/fcb/testcase.yaml b/tests/subsys/settings/fcb/testcase.yaml new file mode 100644 index 00000000000..a2c10d236ff --- /dev/null +++ b/tests/subsys/settings/fcb/testcase.yaml @@ -0,0 +1,5 @@ +tests: + test: + build_only: true + platform_whitelist: nrf52840_pca10056, nrf52_pca10040 + tags: settings_fcb diff --git a/tests/subsys/settings/nffs/CMakeLists.txt b/tests/subsys/settings/nffs/CMakeLists.txt new file mode 100644 index 00000000000..a538605b7de --- /dev/null +++ b/tests/subsys/settings/nffs/CMakeLists.txt @@ -0,0 +1,16 @@ +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c ../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/nffs/prj.conf b/tests/subsys/settings/nffs/prj.conf new file mode 100644 index 00000000000..d5ce30509d7 --- /dev/null +++ b/tests/subsys/settings/nffs/prj.conf @@ -0,0 +1,28 @@ +CONFIG_ZTEST=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_SOC_FLASH_NRF5=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_ARM_CORE_MPU=n +CONFIG_ARM_MPU=n +CONFIG_ARM_MPU_NRF52X=n + +CONFIG_ZTEST_STACKSIZE=2048 +CONFIG_MAIN_STACK_SIZE=1024 +CONFIG_HEAP_MEM_POOL_SIZE=1024 + +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_NFFS=y +CONFIG_FS_NFFS_FLASH_DEV_NAME="NRF_FLASH_DRV_NAME" +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_FILE_SYSTEM_NFFS=y +CONFIG_NFFS_FILESYSTEM_MAX_AREAS=12 + +CONFIG_SETTINGS=y +CONFIG_SETTINGS_FS=y diff --git a/tests/subsys/settings/nffs/src/settings_setup_nffs.c b/tests/subsys/settings/nffs/src/settings_setup_nffs.c new file mode 100644 index 00000000000..862cbb39819 --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_setup_nffs.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include +#include + +/* 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 = TEST_FS_MPTR, + .fs_data = &flash_desc, +}; + +void config_setup_nffs(void) +{ + 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\n"); + + /* 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(TEST_CONFIG_DIR); + zassert_true(rc == 0 || rc == -ENOENT, + "can't delete config directory%d\n", rc); +} diff --git a/tests/subsys/settings/nffs/src/settings_test.h b/tests/subsys/settings/nffs/src/settings_test.h new file mode 100644 index 00000000000..06e0aa309aa --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_test.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _SETTINGS_TEST_FCB_H +#define _SETTINGS_TEST_FCB_H + +#include +#include +#include +#include + +#include "settings/settings.h" + +#ifdef __cplusplus +#extern "C" { +#endif + +#define TEST_FS_MPTR "/nffs" +#define TEST_CONFIG_DIR TEST_FS_MPTR"/config" + +extern u8_t val8; +extern u32_t val32; +extern u64_t val64; + +extern int test_get_called; +extern int test_set_called; +extern int test_commit_called; +extern int test_export_block; + +extern int c2_var_count; + +extern struct settings_handler c_test_handlers[]; + +void ctest_clear_call_state(void); +int ctest_get_call_state(void); + +void config_wipe_srcs(void); + +int fsutil_read_file(const char *path, off_t offset, size_t len, void *dst, + size_t *out_len); +int fsutil_write_file(const char *path, const void *data, size_t len); +int settings_test_file_strstr(const char *fname, char *string); + +#ifdef __cplusplus +} +#endif +#endif /* _SETTINGS_TEST_FCB_H */ diff --git a/tests/subsys/settings/nffs/src/settings_test_compress_file.c b/tests/subsys/settings/nffs/src/settings_test_compress_file.c new file mode 100644 index 00000000000..af508c0fd27 --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_test_compress_file.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "settings_test.h" +#include "settings/settings_file.h" + +int file_str_cmp(const char *fname, char *string); + +void test_config_compress_file(void) +{ + int rc; + struct settings_file cf; + + config_wipe_srcs(); + + rc = fs_mkdir(TEST_CONFIG_DIR); + zassert_true(rc == 0 || rc == -EEXIST, "can't create directory\n"); + + cf.cf_name = TEST_CONFIG_DIR "/korwin"; + cf.cf_maxlines = 24; + rc = settings_file_src(&cf); + zassert_true(rc == 0, "can't register FS as configuration source\n"); + + rc = settings_file_dst(&cf); + zassert_true(rc == 0, + "can't register FS as configuration destination\n"); + + val64 = 1125; + + for (int i = 0; i < 22; i++) { + val8 = i; + rc = settings_save(); + zassert_true(rc == 0, "fs write error\n"); + + val8 = 0xff; + settings_load(); + zassert_true(val8 == i, "Bad value loaded\n"); + } + + val64 = 37; + rc = settings_save(); + zassert_true(rc == 0, "fs write error\n"); + + /* check 1st compression */ + rc = file_str_cmp(cf.cf_name, "myfoo/mybar64=1125\n" \ + "myfoo/mybar=21\n" \ + "myfoo/mybar64=37\n"); + zassert_true(rc == 0, "bad value read\n"); + + for (int i = 0; i < 21; i++) { + val64 = i; + rc = settings_save(); + zassert_true(rc == 0, "fs write error\n"); + + val64 = 0xff; + settings_load(); + zassert_true(val64 == i, "Bad value loaded\n"); + } + + /* check subsequent compression */ + rc = file_str_cmp(cf.cf_name, "myfoo/mybar=21\n" \ + "myfoo/mybar64=19\n" \ + "myfoo/mybar64=20\n"); + zassert_true(rc == 0, "bad value read\n"); +} + +int file_str_cmp(const char *fname, char *string) +{ + int rc; + u32_t len; + u32_t rlen; + char *buf; + struct fs_dirent entry; + + rc = fs_stat(fname, &entry); + if (rc) { + return rc; + } + + if (entry.size != strlen(string)) { + return -1; + } + + len = entry.size; + buf = (char *)k_malloc(len + 1); + zassert_not_null(buf, "out of memory\n"); + + rc = fsutil_read_file(fname, 0, len, buf, &rlen); + zassert_true(rc == 0, "can't access the file\n'"); + zassert_true(rc == 0, "not enough data read\n'"); + buf[rlen] = '\0'; + + if (strcmp(buf, string)) { + return -1; + } + + return -0; +} diff --git a/tests/subsys/settings/nffs/src/settings_test_empty_file.c b/tests/subsys/settings/nffs/src/settings_test_empty_file.c new file mode 100644 index 00000000000..c2b8cfd8801 --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_test_empty_file.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_file.h" +#include + +void test_config_empty_file(void) +{ + int rc; + struct settings_file cf_mfg; + struct settings_file cf_running; + const char cf_mfg_test[] = ""; + const char cf_running_test[] = "\n\n"; + + config_wipe_srcs(); + + cf_mfg.cf_name = TEST_CONFIG_DIR "/mfg"; + cf_running.cf_name = TEST_CONFIG_DIR "/running"; + + rc = settings_file_src(&cf_mfg); + zassert_true(rc == 0, "can't register FS as configuration source\n"); + rc = settings_file_src(&cf_running); + zassert_true(rc == 0, "can't register FS as configuration source\n"); + + /* + * No files + */ + settings_load(); + + rc = fs_mkdir(TEST_CONFIG_DIR); + zassert_true(rc == 0, "can't create directory\n"); + + rc = fsutil_write_file(TEST_CONFIG_DIR "/mfg", cf_mfg_test, + sizeof(cf_mfg_test)); + zassert_true(rc == 0, "can't write to file\n"); + + rc = fsutil_write_file(TEST_CONFIG_DIR "/running", cf_running_test, + sizeof(cf_running_test)); + zassert_true(rc == 0, "can't write to file\n"); + + settings_load(); + config_wipe_srcs(); + ctest_clear_call_state(); +} diff --git a/tests/subsys/settings/nffs/src/settings_test_multiple_in_file.c b/tests/subsys/settings/nffs/src/settings_test_multiple_in_file.c new file mode 100644 index 00000000000..cfacfba253e --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_test_multiple_in_file.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_file.h" + +void test_config_multiple_in_file(void) +{ + int rc; + struct settings_file cf_mfg; + const char cf_mfg_test1[] = + "myfoo/mybar=1\n" + "myfoo/mybar=14"; + const char cf_mfg_test2[] = + "myfoo/mybar=1\n" + "myfoo/mybar=15\n" + "\n"; + + config_wipe_srcs(); + + cf_mfg.cf_name = TEST_CONFIG_DIR "/mfg"; + rc = settings_file_src(&cf_mfg); + zassert_true(rc == 0, "can't register FS as configuration source\n"); + + rc = fsutil_write_file(TEST_CONFIG_DIR "/mfg", cf_mfg_test1, + sizeof(cf_mfg_test1)); + zassert_true(rc == 0, "can't write to file\n"); + + settings_load(); + zassert_true(test_set_called, "the SET handler wasn't called\n"); + zassert_true(val8 == 14, + "SET handler: was called with wrong parameters\n"); + + rc = fsutil_write_file(TEST_CONFIG_DIR "/mfg", cf_mfg_test2, + sizeof(cf_mfg_test2)); + zassert_true(rc == 0, "can't write to file\n"); + + settings_load(); + zassert_true(test_set_called, "the SET handler wasn't called\n"); + zassert_true(val8 == 15, + "SET handler: was called with wrong parameters\n"); +} diff --git a/tests/subsys/settings/nffs/src/settings_test_nffs.c b/tests/subsys/settings/nffs/src/settings_test_nffs.c new file mode 100644 index 00000000000..e2427cb8358 --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_test_nffs.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "settings_test.h" +#include "settings_priv.h" + + +u8_t val8; +u64_t val64; + +int test_get_called; +int test_set_called; +int test_commit_called; +int test_export_block; + +int c2_var_count = 1; + +char *c1_handle_get(int argc, char **argv, char *val, int val_len_max); +int c1_handle_set(int argc, char **argv, char *val); +int c1_handle_commit(void); +int c1_handle_export(void (*cb)(char *name, char *value), + enum settings_export_tgt tgt); + +struct settings_handler c_test_handlers[] = { + { + .name = "myfoo", + .h_get = c1_handle_get, + .h_set = c1_handle_set, + .h_commit = c1_handle_commit, + .h_export = c1_handle_export + }, +}; + + + +char *c1_handle_get(int argc, char **argv, char *val, int val_len_max) +{ + test_get_called = 1; + + if (argc == 1 && !strcmp(argv[0], "mybar")) { + return settings_str_from_value(SETTINGS_INT8, &val8, val, + val_len_max); + } + + if (argc == 1 && !strcmp(argv[0], "mybar64")) { + return settings_str_from_value(SETTINGS_INT64, &val64, val, + val_len_max); + } + + return NULL; +} + +int c1_handle_set(int argc, char **argv, char *val) +{ + u8_t newval; + u64_t newval64; + int rc; + + test_set_called = 1; + if (argc == 1 && !strcmp(argv[0], "mybar")) { + rc = SETTINGS_VALUE_SET(val, SETTINGS_INT8, newval); + zassert_true(rc == 0, "SETTINGS_VALUE_SET callback"); + val8 = newval; + return 0; + } + + if (argc == 1 && !strcmp(argv[0], "mybar64")) { + rc = SETTINGS_VALUE_SET(val, SETTINGS_INT64, newval64); + zassert_true(rc == 0, "SETTINGS_VALUE_SET callback"); + val64 = newval64; + return 0; + } + + return -ENOENT; +} + +int c1_handle_commit(void) +{ + test_commit_called = 1; + return 0; +} + +int c1_handle_export(void (*cb)(char *name, char *value), + enum settings_export_tgt tgt) +{ + char value[32]; + + if (test_export_block) { + return 0; + } + + settings_str_from_value(SETTINGS_INT8, &val8, value, sizeof(value)); + cb("myfoo/mybar", value); + + settings_str_from_value(SETTINGS_INT64, &val64, value, sizeof(value)); + cb("myfoo/mybar64", value); + + return 0; +} + +void ctest_clear_call_state(void) +{ + test_get_called = 0; + test_set_called = 0; + test_commit_called = 0; +} + +int ctest_get_call_state(void) +{ + return test_get_called + test_set_called + test_commit_called; +} + +void config_wipe_srcs(void) +{ + sys_slist_init(&settings_load_srcs); + settings_save_dst = NULL; +} + +int fsutil_read_file(const char *path, off_t offset, size_t len, void *dst, + size_t *out_len) +{ + struct fs_file_t file; + int rc; + ssize_t r_len = 0; + + rc = fs_open(&file, path); + if (rc != 0) { + return rc; + } + + r_len = fs_read(&file, dst, len); + if (r_len < 0) { + rc = -EIO; + } else { + *out_len = r_len; + } + + fs_close(&file); + return rc; +} + +int fsutil_write_file(const char *path, const void *data, size_t len) +{ + struct fs_file_t file; + int rc; + + rc = fs_open(&file, path); + if (rc != 0) { + return rc; + } + + if (fs_write(&file, data, len) != len) { + rc = -EIO; + } + + fs_close(&file); + return rc; +} + +int settings_test_file_strstr(const char *fname, char *string) +{ + int rc; + u32_t len; + u32_t rlen; + char *buf; + struct fs_dirent entry; + + rc = fs_stat(fname, &entry); + if (rc) { + return rc; + } + + len = entry.size; + buf = (char *)k_malloc(len + 1); + zassert_not_null(buf, "out of memory\n"); + + rc = fsutil_read_file(fname, 0, len, buf, &rlen); + zassert_true(rc == 0, "can't access the file\n'"); + zassert_true(rc == 0, "not enough data read\n'"); + buf[rlen] = '\0'; + + if (strstr(buf, string)) { + return 0; + } + + return -1; +} + +void config_empty_lookups(void); +void test_config_insert(void); +void test_config_getset_unknown(void); +void test_config_getset_int(void); +void test_config_getset_bytes(void); +void test_config_getset_int64(void); +void test_config_commit(void); + +void config_setup_nffs(void); +void test_config_empty_file(void); +void test_config_small_file(void); +void test_config_multiple_in_file(void); +void test_config_save_in_file(void); +void test_config_save_one_file(void); +void test_config_compress_file(void); + +void test_main(void *p1, void *p2, void *p3) +{ + ztest_test_suite(test_config_fcb, + /* Config tests */ + ztest_unit_test(config_empty_lookups), + ztest_unit_test(test_config_insert), + ztest_unit_test(test_config_getset_unknown), + ztest_unit_test(test_config_getset_int), + ztest_unit_test(test_config_getset_bytes), + ztest_unit_test(test_config_getset_int64), + ztest_unit_test(test_config_commit), + /* NFFS as backing storage. */ + ztest_unit_test(config_setup_nffs), + ztest_unit_test(test_config_empty_file), + ztest_unit_test(test_config_small_file), + ztest_unit_test(test_config_multiple_in_file), + ztest_unit_test(test_config_save_in_file), + ztest_unit_test(test_config_save_one_file), + ztest_unit_test(test_config_compress_file) + ); + + ztest_run_test_suite(test_config_fcb); +} diff --git a/tests/subsys/settings/nffs/src/settings_test_save_in_file.c b/tests/subsys/settings/nffs/src/settings_test_save_in_file.c new file mode 100644 index 00000000000..a5ea936e778 --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_test_save_in_file.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_file.h" + +void test_config_save_in_file(void) +{ + int rc; + struct settings_file cf; + + config_wipe_srcs(); + + rc = fs_mkdir(TEST_CONFIG_DIR); + zassert_true(rc == 0 || rc == -EEXIST, "can't create directory\n"); + + cf.cf_name = TEST_CONFIG_DIR "/blah"; + rc = settings_file_src(&cf); + zassert_true(rc == 0, "can't register FS as configuration source\n"); + + rc = settings_file_dst(&cf); + zassert_true(rc == 0, + "can't register FS as configuration destination\n"); + + val8 = 8; + rc = settings_save(); + zassert_true(rc == 0, "fs write error\n"); + + rc = settings_test_file_strstr(cf.cf_name, "myfoo/mybar=8\n"); + zassert_true(rc == 0, "bad value read\n"); + + val8 = 43; + rc = settings_save(); + zassert_true(rc == 0, "fs write error\n"); + + rc = settings_test_file_strstr(cf.cf_name, "myfoo/mybar=43\n"); + zassert_true(rc == 0, "bad value read\n"); +} diff --git a/tests/subsys/settings/nffs/src/settings_test_save_one_file.c b/tests/subsys/settings/nffs/src/settings_test_save_one_file.c new file mode 100644 index 00000000000..f7ec3f74328 --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_test_save_one_file.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_file.h" + +void test_config_save_one_file(void) +{ + int rc; + struct settings_file cf; + + config_wipe_srcs(); + rc = fs_mkdir(TEST_CONFIG_DIR); + zassert_true(rc == 0 || rc == -EEXIST, "can't create directory\n"); + + cf.cf_name = TEST_CONFIG_DIR "/blah"; + rc = settings_file_src(&cf); + zassert_true(rc == 0, "can't register FS as configuration source\n"); + + rc = settings_file_dst(&cf); + zassert_true(rc == 0, + "can't register FS as configuration destination\n"); + + val8 = 33; + rc = settings_save(); + zassert_true(rc == 0, "fs write error\n"); + + rc = settings_save_one("myfoo/mybar", "42"); + zassert_equal(rc, 0, "fs one item write error\n"); + + rc = settings_load(); + zassert_true(rc == 0, "fs redout error\n"); + zassert_true(val8 == 42, "bad value read\n"); + + rc = settings_save_one("myfoo/mybar", "44"); + zassert_true(rc == 0, "fs one item write error\n"); + + rc = settings_load(); + zassert_true(rc == 0, "fs redout error\n"); + zassert_true(val8 == 44, "bad value read\n"); +} diff --git a/tests/subsys/settings/nffs/src/settings_test_small_file.c b/tests/subsys/settings/nffs/src/settings_test_small_file.c new file mode 100644 index 00000000000..d2ab1ab83aa --- /dev/null +++ b/tests/subsys/settings/nffs/src/settings_test_small_file.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include "settings/settings_file.h" + +void test_config_small_file(void) +{ + int rc; + struct settings_file cf_mfg; + struct settings_file cf_running; + const char cf_mfg_test[] = "myfoo/mybar=1"; + const char cf_running_test[] = " myfoo/mybar = 8 "; + + config_wipe_srcs(); + + cf_mfg.cf_name = TEST_CONFIG_DIR "/mfg"; + cf_running.cf_name = TEST_CONFIG_DIR "/running"; + + rc = settings_file_src(&cf_mfg); + zassert_true(rc == 0, "can't register FS as configuration source\n"); + rc = settings_file_src(&cf_running); + zassert_true(rc == 0, "can't register FS as configuration source\n"); + + rc = fsutil_write_file(TEST_CONFIG_DIR "/mfg", cf_mfg_test, + sizeof(cf_mfg_test)); + zassert_true(rc == 0, "can't write to file\n"); + + settings_load(); + zassert_true(test_set_called, "the SET handler wasn't called\n"); + zassert_true(val8 == 1, + "SET handler: was called with wrong parameters\n"); + + ctest_clear_call_state(); + + rc = fsutil_write_file(TEST_CONFIG_DIR "/running", cf_running_test, + sizeof(cf_running_test)); + zassert_true(rc == 0, "can't write to file\n"); + + settings_load(); + zassert_true(test_set_called, "the SET handler wasn't called\n"); + zassert_true(val8 == 8, + "SET handler: was called with wrong parameters\n"); + + ctest_clear_call_state(); +} diff --git a/tests/subsys/settings/nffs/testcase.yaml b/tests/subsys/settings/nffs/testcase.yaml new file mode 100644 index 00000000000..b09eca18a57 --- /dev/null +++ b/tests/subsys/settings/nffs/testcase.yaml @@ -0,0 +1,5 @@ +tests: + test: + build_only: true + platform_whitelist: nrf52840_pca10056, nrf52_pca10040 + tags: settings_fs diff --git a/tests/subsys/settings/src/settings_empty_lookups.c b/tests/subsys/settings/src/settings_empty_lookups.c new file mode 100644 index 00000000000..dadbbe3c603 --- /dev/null +++ b/tests/subsys/settings/src/settings_empty_lookups.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" + +void config_empty_lookups(void) +{ + int rc; + char name[80]; + char tmp[64], *str; + + strcpy(name, "foo/bar"); + rc = settings_set_value(name, "tmp"); + zassert_true(rc != 0, "settings_set_value callback"); + + strcpy(name, "foo/bar"); + str = settings_get_value(name, tmp, sizeof(tmp)); + zassert_true(str == NULL, "settings_get_value callback"); +} diff --git a/tests/subsys/settings/src/settings_test_commit.c b/tests/subsys/settings/src/settings_test_commit.c new file mode 100644 index 00000000000..17bac1ddc0b --- /dev/null +++ b/tests/subsys/settings/src/settings_test_commit.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" + +void test_config_commit(void) +{ + char name[80]; + int rc; + + strcpy(name, "bar"); + rc = settings_commit(name); + zassert_true(rc, "commit-nonexisting-tree call should succeed\n"); + zassert_true(ctest_get_call_state() == 0, + "a handler was called unexpectedly\n"); + + rc = settings_commit(NULL); + zassert_true(rc == 0, "commit-All call should succeed"); + zassert_true(test_commit_called == 1, + "the COMMIT handler wasn't called\n"); + ctest_clear_call_state(); + + strcpy(name, "myfoo"); + rc = settings_commit(name); + zassert_true(rc == 0, "commit-a-tree call should succeed\n"); + zassert_true(test_commit_called == 1, + "the COMMIT handler wasn't called\n"); + ctest_clear_call_state(); +} diff --git a/tests/subsys/settings/src/settings_test_getset_bytes.c b/tests/subsys/settings/src/settings_test_getset_bytes.c new file mode 100644 index 00000000000..586b1b87047 --- /dev/null +++ b/tests/subsys/settings/src/settings_test_getset_bytes.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" + +void test_config_getset_bytes(void) +{ + char orig[32]; + char bytes[32]; + char str[48]; + char *ret; + int j, i; + int tmp; + int rc; + + for (j = 1; j < sizeof(orig); j++) { + for (i = 0; i < j; i++) { + orig[i] = i + j + 1; + } + ret = settings_str_from_bytes(orig, j, str, sizeof(str)); + zassert_not_null(ret, "string base64 encodding\n"); + tmp = strlen(str); + zassert_true(tmp < sizeof(str), "encoded string is to long\n"); + + memset(bytes, 0, sizeof(bytes)); + tmp = sizeof(bytes); + + tmp = sizeof(bytes); + rc = settings_bytes_from_str(str, bytes, &tmp); + zassert_true(rc == 0, "base64 to string decodding\n"); + zassert_true(tmp == j, "decoded string bad length\n"); + zassert_true(!memcmp(orig, bytes, j), + "decoded string not match to origin\n"); + } +} diff --git a/tests/subsys/settings/src/settings_test_getset_int.c b/tests/subsys/settings/src/settings_test_getset_int.c new file mode 100644 index 00000000000..1d99ff2378f --- /dev/null +++ b/tests/subsys/settings/src/settings_test_getset_int.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" + +void test_config_getset_int(void) +{ + char name[80]; + char tmp[64], *str; + int rc; + + strcpy(name, "myfoo/mybar"); + rc = settings_set_value(name, "42"); + zassert_true(rc == 0, "can not set key value\n"); + zassert_true(test_set_called == 1, "the SET handler wasn't called\n"); + zassert_true(val8 == 42, + "SET handler: was called with wrong parameters\n"); + ctest_clear_call_state(); + + strcpy(name, "myfoo/mybar"); + str = settings_get_value(name, tmp, sizeof(tmp)); + zassert_not_null(str, "the key value should been available\n"); + zassert_true(test_get_called == 1, "the GET handler wasn't called\n"); + zassert_true(!strcmp("42", tmp), "unexpected value fetched\n"); + ctest_clear_call_state(); +} diff --git a/tests/subsys/settings/src/settings_test_getset_int64.c b/tests/subsys/settings/src/settings_test_getset_int64.c new file mode 100644 index 00000000000..46346f629f6 --- /dev/null +++ b/tests/subsys/settings/src/settings_test_getset_int64.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" + +void test_config_getset_int64(void) +{ + char name[80]; + char tmp[64], *str; + int rc; + s64_t cmp; + + strcpy(name, "myfoo/mybar64"); + rc = settings_set_value(name, "-9218247941279444428"); + zassert_true(rc == 0, "can't set value\n"); + zassert_true(test_set_called == 1, "the SET handler wasn't called\n"); + cmp = 0x8012345678901234; + zassert_true(memcmp(&val64, &cmp, sizeof(val64)) == 0, + "SET handler: was called with wrong parameters\n"); + ctest_clear_call_state(); + + strcpy(name, "myfoo/mybar64"); + str = settings_get_value(name, tmp, sizeof(tmp)); + zassert_not_null(str, "the key value should been available\n"); + zassert_true(test_get_called == 1, "the GET handler wasn't called\n"); + zassert_true(!strcmp("-9218247941279444428", tmp), + "unexpected value fetched %s\n", tmp); + ctest_clear_call_state(); + + strcpy(name, "myfoo/mybar64"); + rc = settings_set_value(name, "1"); + zassert_true(rc == 0, "can't set value\n"); + zassert_true(test_set_called == 1, "the SET handler wasn't called\n"); + zassert_true(val64 == 1, + "SET handler: was called with wrong parameters\n"); + ctest_clear_call_state(); + + strcpy(name, "myfoo/mybar64"); + str = settings_get_value(name, tmp, sizeof(tmp)); + zassert_not_null(str, "the key value should been available\n"); + zassert_true(test_get_called == 1, "the GET handler wasn't called\n"); + zassert_true(!strcmp("1", tmp), "unexpected value fetched\n"); + ctest_clear_call_state(); +} diff --git a/tests/subsys/settings/src/settings_test_getset_unknown.c b/tests/subsys/settings/src/settings_test_getset_unknown.c new file mode 100644 index 00000000000..c96a65eed92 --- /dev/null +++ b/tests/subsys/settings/src/settings_test_getset_unknown.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" +#include + +void test_config_getset_unknown(void) +{ + char name[80]; + char tmp[64], *str; + int rc; + + strcpy(name, "foo/bar"); + rc = settings_set_value(name, "tmp"); + zassert_true(rc != 0, "set value should fail\n"); + zassert_true(ctest_get_call_state() == 0, + "a handler was called unexpectedly\n"); + + strcpy(name, "foo/bar"); + str = settings_get_value(name, tmp, sizeof(tmp)); + zassert_true(str == NULL, "value should been unreachable\n"); + zassert_true(ctest_get_call_state() == 0, + "a handler was called unexpectedly\n"); + + strcpy(name, "myfoo/bar"); + rc = settings_set_value(name, "tmp"); + zassert_true(rc == -ENOENT, "unexpected failure retval\n"); + zassert_true(test_set_called == 1, + "the GET handler wasn't called\n"); + ctest_clear_call_state(); + + strcpy(name, "myfoo/bar"); + str = settings_get_value(name, tmp, sizeof(tmp)); + zassert_true(str == NULL, "value should been unreachable\n"); + zassert_true(test_get_called == 1, + "the SET handler wasn't called\n"); + ctest_clear_call_state(); +} diff --git a/tests/subsys/settings/src/settings_test_insert.c b/tests/subsys/settings/src/settings_test_insert.c new file mode 100644 index 00000000000..b0e1922a64f --- /dev/null +++ b/tests/subsys/settings/src/settings_test_insert.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "settings_test.h" + +void test_config_insert_x(int idx) +{ + int rc; + + rc = settings_register(&c_test_handlers[idx]); + zassert_true(rc == 0, "settings_register fail"); +} + +void test_config_insert(void) +{ + test_config_insert_x(0); +} + +void test_config_insert2(void) +{ + test_config_insert_x(1); +} + +void test_config_insert3(void) +{ + test_config_insert_x(2); +}