diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/add_del/add_del.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/add_del/add_del.cpp new file mode 100644 index 0000000000..32a95581e6 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/add_del/add_del.cpp @@ -0,0 +1,309 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** @file add_del.cpp Test cases to add and delete key-value pairs in the CFSTORE. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "Driver_Common.h" +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_add_del_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +/* Create the main box ACL list for the application. + * The main ACL gets inherited by all the other boxes + */ +CFSTORE_UVISOR_MAIN_ACL(cfstore_acl_uvisor_box_add_del_g); + +/* Enable uVisor. */ +UVISOR_SET_MODE_ACL(UVISOR_ENABLED, cfstore_acl_uvisor_box_add_del_g); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +static cfstore_kv_data_t cfstore_add_del_test_07_data[] = { + CFSTORE_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_add_del_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + return CaseNext; +} + +/** @brief + * + * This test case does the following: + * - creates a KV. + * - deletes the KV. + * - checks that the deleted KV can no longer be found in the store. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_add_del_test_01_end(const size_t call_count) +{ + bool bfound = false; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_add_del_test_07_data[0].value); + + ret = cfstore_test_create(cfstore_add_del_test_07_data[0].key_name, (char*) cfstore_add_del_test_07_data[0].value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + /* now delete KV*/ + ret = drv->Open(cfstore_add_del_test_07_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to Open() (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + if(hkey != NULL){ + ret = drv->Delete(hkey); + drv->Close(hkey); + hkey = NULL; + } + /* check that the KV has been deleted */ + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("LOG: WARNING: About to look for non-existent key (key_name=%s) (which will generate internal trace reporting errors if debug trace enabled).\n", cfstore_add_del_test_07_data[0].key_name); + ret = cfstore_test_kv_is_found(cfstore_add_del_test_07_data[0].key_name, &bfound); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error failed to delete a key (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_add_del_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Test failed: found KV that was previously deleted (key_name=%s)\n", __func__, cfstore_add_del_test_07_data[0].key_name); + TEST_ASSERT_MESSAGE(bfound == false, cfstore_add_del_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + return CaseNext; +} + + +static cfstore_kv_data_t cfstore_add_del_test_08_data[] = { + CFSTORE_INIT_1_TABLE_HEAD, + CFSTORE_INIT_1_TABLE_MID_NODE, + CFSTORE_INIT_1_TABLE_TAIL, + { NULL, NULL}, +}; + + +/** @brief + * + * This test case adds a small number of KVs (~3), and then delete them. + * - add key(s) + * - delete key(s) + * - make sure can't find key in cfstore + * - loop over the above a number of times. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_add_del_test_02_end(const size_t call_count) +{ + bool bResult = true; // We'll do "&=" cumulative checking. + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_KEYDESC kdesc; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + + /* create */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + node = cfstore_add_del_test_08_data; + while(node->key_name != NULL) + { + len = strlen(node->value); + ret = cfstore_test_create(node->key_name, (char*) node->value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create kv (key_name=%s.\n", __func__, node->key_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("Created KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + node++; + } + + /* test delete all */ + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete all KVs.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + /* check there are no KVs present as expected */ + node = cfstore_add_del_test_08_data; + while(node->key_name != NULL) + { + ret = cfstore_test_kv_is_found(node->key_name, &bResult); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: found key when should not be present.\n", __func__); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND && bResult == false, cfstore_add_del_utest_msg_g); + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("Found KV successfully (key_name=\"%s\")\n", node->key_name); + node++; + } + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + return CaseNext; +} + +/** @brief + * + * This test case adds ~50 KVs, and then delete entries at the start, + * middle and end of list. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_add_del_test_03_end(const size_t call_count) +{ + bool bfound = false; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_FMODE flags; + cfstore_kv_data_t *node; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&flags, 0, sizeof(flags)); + + ret = cfstore_test_init_1(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialise cfstore area with entries\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + /* delete some keys */ + node = cfstore_add_del_test_08_data; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:about to delete key (key_name=%s).\n", __func__, node->key_name); + cfstore_test_delete(node->key_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error failed to delete a key (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("Deleted KV successfully (key_name=\"%s\")\n", node->key_name); + node++; + } + /* check the keys have been deleted */ + node = cfstore_add_del_test_08_data; + while(node->key_name != NULL) + { + ret = cfstore_test_kv_is_found(node->key_name, &bfound); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete a key (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_add_del_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Test failed: found KV that was previously deleted (key_name=%s)\n", __func__, node->key_name); + TEST_ASSERT_MESSAGE(bfound == false, cfstore_add_del_utest_msg_g); + node++; + } + + /* clean up by deleting all remaining KVs. this is not part of the test */ + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error failed to delete a all KVs (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + return CaseNext; +} + + +/** @brief + * + * This test case is as per test_03 but using delete_all() on all init_1 data. + * This test case is yet to be implemented. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_add_del_test_04(const size_t call_count) +{ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_DBGLOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_add_del_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("ADD_DEL_test_00", cfstore_add_del_test_00), + Case("ADD_DEL_test_01_start", cfstore_utest_default_start), + Case("ADD_DEL_test_01_end", cfstore_add_del_test_01_end), + Case("ADD_DEL_test_02_start", cfstore_utest_default_start), + Case("ADD_DEL_test_02_end", cfstore_add_del_test_02_end), + Case("ADD_DEL_test_03_start", cfstore_utest_default_start), + Case("ADD_DEL_test_03_end", cfstore_add_del_test_03_end), + Case("ADD_DEL_test_04", cfstore_add_del_test_04), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/close/close.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/close/close.cpp new file mode 100644 index 0000000000..341915ea0d --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/close/close.cpp @@ -0,0 +1,251 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file close.cpp Test cases to close KVs in the CFSTORE using the drv->Close() API function. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_close_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.close.box1"); +UVISOR_BOX_CONFIG(cfstore_close_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_close_test_01_kv_data[] = { + { "yotta.hello-world.animal{wobbly-dog}{foot}frontLeft", "first_data_"}, + { "yotta.hello-world.animal{wobbly-dog}{foot}frontLeft", "second_data"}, + { NULL, NULL}, +}; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_close_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + return CaseNext; +} + + +/** @brief + * + * The is a basic test case which does the following: + * - 01. create a key with handle hkey1 + * - 02. write data of hkey + * - 03. opens KV with 2nd handle, hkey2 + * - 04. read data with hkey2 and make sure its the same as that written with hkey1 + * - 05. write new data with hkey2 + * - 06. delete hkey2 + * - 07. close hkey2 + * - 08. read hkey1 and make sure the data is the newly written data i.e. the key hasnt + * been deleted yet + * - 09. try to open KV and make sure unable to do so, as KV is deleting + * - 10. close hkey1 + * - 11. try to open KV and make sure unable to do so because its now been deleted + * - 12. create new KV with same key_name to make sure can re-create the key again. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_close_test_01_end(const size_t call_count) +{ + char read_buf[CFSTORE_KEY_NAME_MAX_LENGTH]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey1); + ARM_CFSTORE_HANDLE_INIT(hkey2); + cfstore_kv_data_t *node; + ARM_CFSTORE_FMODE flags; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + node = &cfstore_close_test_01_kv_data[0]; + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + memset(read_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH); + + /* step 01 */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + CFSTORE_DBGLOG("%s:About to create new node (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + ret = drv->Create(node->key_name, strlen(node->value), &kdesc, hkey1); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create node (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + CFSTORE_DBGLOG("%s:length of KV=%d (key_name=\"%s\", value=\"%s\")\n", __func__, (int) len, node->key_name, node->value); + + /* step 02 */ + len = strlen(node->value); + ret = drv->Write(hkey1, (char*) node->value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d, (ret=%d)\n", __func__, node->key_name, node->value, (int) len, (int) ret); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_close_utest_msg_g); + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("Created KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + + /* step 03: Now open second handle while keeping first handle open */ + flags.read = true; + flags.write = true; + ret = drv->Open(node->key_name, flags, hkey2); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + len = strlen(node->value) + 1; + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("Opened KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + + /* step 04 */ + ret = drv->Read(hkey2, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read key (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* check read data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\n", __func__, read_buf, node->key_name, node->value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, cfstore_close_utest_msg_g); + + /* step 05 write new data using hkey2 */ + node = &cfstore_close_test_01_kv_data[1]; + len = strlen(node->value); + ret = drv->Write(hkey2, (char*) node->value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key with 2nd handle (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d, (ret=%d)\n", __func__, node->key_name, node->value, (int) len, (int) ret); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_close_utest_msg_g); + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("Wrote KV successfully with new data (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + + /* step 06 delete hkey2 */ + ret = drv->Delete(hkey2); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to Delete KV with 2nd handle (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* step 07 close hkey2 */ + ret = drv->Close(hkey2); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to Close KV with 2nd handle (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* step 08 read hkey1 for updated data */ + len = strlen(node->value) + 1; + memset(read_buf, 0, len); + ret = drv->Read(hkey1, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read key with hkey1 (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* check read data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\n", __func__, read_buf, node->key_name, node->value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, cfstore_close_utest_msg_g); + + /* step 09 */ + ret = drv->Open(node->key_name, flags, hkey2); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Should not be able to open pre-existing key in deleting state (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_PREEXISTING_KEY_DELETING || ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_close_utest_msg_g); + + /* step 10 close hkey1 */ + ret = drv->Close(hkey1); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to Close KV with 1st handle (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* step 11 try to open KV and make sure unable to do so because its now been deleted */ + ret = drv->Open(node->key_name, flags, hkey1); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Should not be able to open key as it should not be present in the CFSTORE (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_close_utest_msg_g); + + /* step 12. create new KV with same key_name to make sure can re-create the key again. */ + node = &cfstore_close_test_01_kv_data[0]; + len = strlen(node->value); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + CFSTORE_DBGLOG("%s:About to create new node (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + ret = drv->Create(node->key_name, strlen(node->value), &kdesc, hkey1); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create node (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + CFSTORE_DBGLOG("%s:length of KV=%d (key_name=\"%s\", value=\"%s\")\n", __func__, (int) len, node->key_name, node->value); + + len = strlen(node->value); + ret = drv->Write(hkey1, (char*) node->value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d, (ret=%d)\n", __func__, node->key_name, node->value, (int) len, (int) ret); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_close_utest_msg_g); + + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("Created KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("CLOSE_test_00", cfstore_close_test_00), + Case("CLOSE_test_01_start", cfstore_utest_default_start), + Case("CLOSE_test_01_end", cfstore_close_test_01_end) +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + + diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/create/create.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/create/create.cpp new file mode 100644 index 0000000000..400e2ea79a --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/create/create.cpp @@ -0,0 +1,700 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Test cases to create KVs in the CFSTORE using the drv->Create() API call. + */ + +/** @file create.cpp Test cases to close KVs in the CFSTORE using the + * drv->Create() API function. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +#ifdef CFSTORE_DEBUG +#define CFSTORE_CREATE_GREENTEA_TIMEOUT_S 360 +#else +#define CFSTORE_CREATE_GREENTEA_TIMEOUT_S 60 +#endif + +static char cfstore_create_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.create.box1"); +UVISOR_BOX_CONFIG(cfstore_create_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/// @cond CFSTORE_DOXYGEN_DISABLE +#define CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01 { "Lefkada.Vathi.Meganisi.Atokos.Vathi.Ithaki.PeriPigathi.AgiosAndreas.Sami.Kefalonia.AgiaEffimia.AgiaSofia.Fiskardo.Frikes.Kioni.Meganissi.Lefkada", "Penelope"} +#define CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02 { "Iolcus.Lemnos.Salmydessus.Cyzicus.Cios.Berbryces.Symplegadese.IsleAres.Colchis.Anenea.Sirens.Scylia.Charybdis.Phaeacia.Triton.Iolcus", "Medea"} +#define CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03 { "338.Chaeronea.336.Macedonia.334.Granicus.333.Issus.332.Tyre.331.Gaugamela.330.Persepolis.Philotas.Parmenion.329.Bactria.Oxus.Samarkand.328.Cleitus.327.Roxane.326.Hydaspes.Bucephalus.324.Hephaestion.323.AlexanderDies", "TheGreat"} +#define CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01 { "0123456789abcdef0123456", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02 { "0123456789abcdef0123456", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"} +#define CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03 { "0123456789abcdef0123456", "nopqrstuvwxyz"} +#define CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01 { "Time.Will.Explain.It.All", "Aegeus"} +#define CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02 { "Cleverness.Is.Not.Wisdom", "Bacchae"} +#define CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03 { "Talk.Sense.To.A.Fool.And.He.Calls.You.Foolish", "Bacchae"} +#define CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04 { "There.is.in.the.worst.of.fortune.the.best.of.chances.for.a.happy.change", "Iphigenia.in.Tauris"} + +static cfstore_kv_data_t cfstore_create_test_01_data[] = { + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03, + { NULL, NULL}, +}; + +/* table 1: to initialise cfstore with CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01 */ +static cfstore_kv_data_t cfstore_create_test_01_data_step_01[] = { + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04, + { NULL, NULL}, +}; + +/* table 2: to CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01 grown to CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02 */ +static cfstore_kv_data_t cfstore_create_test_01_data_step_02[] = { + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04, + { NULL, NULL}, +}; + +/* table 3: to CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02 shrunk to CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03 */ +static cfstore_kv_data_t cfstore_create_test_01_data_step_03[] = { + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04, + { NULL, NULL}, +}; + +/* table 3: CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03 deleted */ +static cfstore_kv_data_t cfstore_create_test_01_data_step_04[] = { + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04, + { NULL, NULL}, +}; +/// @endcond + +/* support functions */ + +/* @brief support function for generating value blob data */ +static int32_t cfstore_create_kv_value_gen(char* value, const size_t len) +{ + size_t i = 0; + size_t cpy_size = 0; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: value pointer is null.\n", __func__); + TEST_ASSERT_MESSAGE(value != NULL, cfstore_create_utest_msg_g); + + while(i < len) + { + cpy_size = len - i > CFSTORE_TEST_BYTE_DATA_TABLE_SIZE ? CFSTORE_TEST_BYTE_DATA_TABLE_SIZE : len - i; + memcpy(value + i, cfstore_test_byte_data_table, cpy_size); + i += cpy_size; + } + return ARM_DRIVER_OK; +} + + +static char* CFSTORE_CREATE_KV_CREATE_NO_TAG = NULL; + +/** @brief + * + * support function to create a KV + * - a kv name is generated with the length name_len + * - a kv value blob is generated with the length value_len + * + * @param name_len the length of the kv_name + * @param name_tag tag to append to name, intended to enable large number of unique strings + * @param value_buf buffer to use for storing the generated value data + * @param value_len the length of the value to generate + * + */ +static int32_t cfstore_create_kv_create(size_t name_len, char* name_tag, char* value_buf, size_t value_len) +{ + int32_t ret = ARM_DRIVER_OK; + size_t name_len_ex = name_len; + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + memset(kv_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(&kdesc, 0, sizeof(kdesc)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + + name_len_ex = name_len; + if(name_tag){ + name_len_ex += strlen(name_tag); + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: max supported KV name length for testing exceeded.\n", __func__); + TEST_ASSERT_MESSAGE(name_len_ex < CFSTORE_KEY_NAME_MAX_LENGTH+1, cfstore_create_utest_msg_g); + + ret = cfstore_test_kv_name_gen(kv_name, name_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_create_utest_msg_g); + + /* append name tag */ + if(name_tag){ + strncat(kv_name, name_tag, CFSTORE_KEY_NAME_MAX_LENGTH); + } + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: kv_name is not the correct length (name_len_ex=%d, expected=%d).\n", __func__, (int) strlen(kv_name), (int) name_len_ex); + TEST_ASSERT_MESSAGE(strlen(kv_name) == name_len_ex, cfstore_create_utest_msg_g); + + ret = cfstore_create_kv_value_gen(value_buf, value_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_create_utest_msg_g); + + ret = cfstore_test_create(kv_name, value_buf, &value_len, &kdesc); + + if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ + CFSTORE_ERRLOG("%s: Error: out of memory\n", __func__); + return ret; + } + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store for kv_name_good(ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + return ret; +} + + +/* @brief cfstore_create_test_01() support function change the size of a value blob in the cfstore */ +static int32_t cfstore_create_test_KV_change(const cfstore_kv_data_t* old_node, const cfstore_kv_data_t* new_node ) +{ + int32_t ret = ARM_DRIVER_ERROR; + size_t len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + memset(&flags, 0, sizeof(flags)); + memset(&kdesc, 0, sizeof(kdesc)); + + /* check node key_names are identical */ + if(strncmp(old_node->key_name, new_node->key_name, strlen(old_node->key_name)) != 0){ + CFSTORE_ERRLOG("%s:old and new entries so not have the same key_name (old_key_name=%s, new_key_name=%s).\n", __func__, old_node->key_name, new_node->key_name); + return ret; + } + len = strlen(new_node->value); + /* supply NULL key descriptor to open a pre-existing key for increasing the blob size */ + ret = drv->Create(new_node->key_name, len, NULL, hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to change size of KV (key_name=%s)(ret=%d).\n", __func__, new_node->key_name, (int) ret); + goto out1; + } + len = strlen(new_node->value); + ret = drv->Write(hkey, new_node->value, &len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to write KV (key_name=%s)(ret=%d).\n", __func__, new_node->key_name, (int) ret); + goto out2; + } + if(len != strlen(new_node->value)){ + CFSTORE_DBGLOG("%s:Failed wrote (%d) rather than the correct number of bytes (%d).\n", __func__, (int) len, (int) strlen(cfstore_create_test_01_data[1].value)); + goto out2; + } +out2: + drv->Close(hkey); +out1: + return ret; +} + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_create_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +/** @brief + * + * Test case to change the value blob size of pre-existing key. + * The test does the following: + * - creates a cfstore with ~10 entries. + * - for a mid-cfstore entry, grow the value blob size + * - check all the cfstore entries can be read correctly and their + * data agrees with the data supplied upon creation. + * - grow the mid-entry value blob size to be ~double the initial size. + * - check all the cfstore entries can be read correctly and their + * data agrees with the data supplied upon creation. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_create_test_01_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_FMODE flags; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&flags, 0, sizeof(flags)); + + ret = cfstore_test_create_table(cfstore_create_test_01_data_step_01); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to add cfstore_create_test_01_data_head (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + /* find cfstore_create_test_01_data[0] and grow the KV MID_ENTRY_01 to MID_ENTRY_02 */ + ret = cfstore_create_test_KV_change(&cfstore_create_test_01_data[0], &cfstore_create_test_01_data[1]); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to increase size of KV (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + /* Now check that the KVs are all present and correct */ + node = cfstore_create_test_01_data_step_02; + while(node->key_name != NULL) + { + ret = cfstore_test_check_node_correct(node); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + node++; + } + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("KV successfully increased in size and other KVs remained unchanged.%s", "\n"); + /* Shrink the KV from KV MID_ENTRY_02 to MID_ENTRY_03 */ + ret = cfstore_create_test_KV_change(&cfstore_create_test_01_data[1], &cfstore_create_test_01_data[2]); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to decrease size of KV (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + /* Now check that the KVs are all present and correct */ + node = cfstore_create_test_01_data_step_03; + while(node->key_name != NULL) + { + ret = cfstore_test_check_node_correct(node); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + node++; + } + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("KV successfully decreased in size and other KVs remained unchanged.%s", "\n"); + + /* Delete the KV */ + ret = cfstore_test_delete(cfstore_create_test_01_data[2].key_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:failed to delete node(key_name=\"%s\")\n", __func__, node->key_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + /* Now check that the KVs are all present and correct */ + node = cfstore_create_test_01_data_step_04; + while(node->key_name != NULL) + { + ret = cfstore_test_check_node_correct(node); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + node++; + } + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +static int32_t cfstore_create_test_02_core(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + uint32_t i = 0; + uint32_t bytes_stored = 0; + const uint32_t max_num_kvs_create = 10; + const size_t kv_name_min_len = CFSTORE_KEY_NAME_MAX_LENGTH - max_num_kvs_create; + const size_t kv_value_min_len = CFSTORE_TEST_BYTE_DATA_TABLE_SIZE; + const size_t max_value_buf_size = kv_value_min_len * (max_num_kvs_create +1); + char value_buf[max_value_buf_size]; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(value_buf, 0, max_value_buf_size); + + for(i = 0; i < max_num_kvs_create; i++) + { + memset(value_buf, 0, max_value_buf_size); + ret = cfstore_create_kv_create(kv_name_min_len +i, CFSTORE_CREATE_KV_CREATE_NO_TAG, value_buf, kv_value_min_len * (i+1)); + bytes_stored += kv_name_min_len + i; /* kv_name */ + bytes_stored += kv_value_min_len * (i+1); /* kv value blob */ + bytes_stored += 8; /* kv overhead */ + if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ + CFSTORE_ERRLOG("Out of memory on %d-th KV, trying to allocate memory totalling %d.\n", (int) i, (int) bytes_stored); + break; + } + CFSTORE_DBGLOG("Successfully stored %d-th KV bytes, totalling %d.\n", (int) i, (int) bytes_stored); + } + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete_all() attributes to clean up after test (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return ret; +} + + +/**@brief + * + * Test case to create ~10 kvs. The amount of data store in the cfstore is as follows: + * - 10 kvs + * - kv name lengths are ~220 => ~ 2200 bytes + * - value blob length is 1x256, 2x256, 3x256, ... 10x256)) = 256(1+2+3+4+..10) = 256*10*11/2 = 14080 + * - kv overhead = 8bytes/kv = 8 * 10 = 80bytes + * - total = (220*10)+256*10*11/2 10*8 = 143800 bytes + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_create_test_02_end(const size_t call_count) +{ + int32_t ret; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + ret = cfstore_create_test_02_core(call_count); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: something went wrong (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +/**@brief + * + * Test to create the ~100 kvs to make the device run out of memory. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_create_test_03_end(const size_t call_count) +{ + int32_t i = 0; + int32_t ret; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + for(i = 0; i < 100; i++) + { + ret = cfstore_create_test_02_core(call_count); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: something went wrong (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("Successfully completed create/destroy loop %d.\n", (int) i); + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +/**@brief + * + * Test to create the 100 kvs to make the device run out of memory. + * The amount of data store in the cfstore is as follows: + * - total = (220*100)+256*100*101/2 100*8 = 1315600 = 1.315x10^6 + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_create_test_04_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + uint32_t i = 0; + uint32_t bytes_stored = 0; + const uint32_t max_num_kvs_create = 100; + const size_t kv_name_min_len = CFSTORE_KEY_NAME_MAX_LENGTH - max_num_kvs_create; + const size_t kv_value_min_len = CFSTORE_TEST_BYTE_DATA_TABLE_SIZE; + const size_t max_value_buf_size = kv_value_min_len/8 * (max_num_kvs_create +1); + char* value_buf = NULL; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + CFSTORE_LOG("%s: cfstore_test_dump: dump here contents of CFSTORE so we know whats present\n", __func__); + ret = cfstore_test_dump(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.1.1 cfstore_test_dump failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + value_buf = (char*) malloc(max_value_buf_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: out of memory.\n", __func__); + TEST_ASSERT_MESSAGE(value_buf != NULL, cfstore_create_utest_msg_g); + + for(i = 0; i < max_num_kvs_create; i++) + { + memset(value_buf, 0, max_value_buf_size); + ret = cfstore_create_kv_create(kv_name_min_len +i, CFSTORE_CREATE_KV_CREATE_NO_TAG, value_buf, kv_value_min_len/8 * (i+1)); + bytes_stored += kv_name_min_len + i; /* kv_name */ + bytes_stored += kv_value_min_len/8 * (i+1); /* kv value blob */ + bytes_stored += 8; /* kv overhead */ + if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ + CFSTORE_ERRLOG("Out of memory on %d-th KV, trying to allocate memory totalling %d.\n", (int) i, (int) bytes_stored); + break; + } + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("Successfully stored %d-th KV bytes, totalling %d.\n", (int) i, (int) bytes_stored); + } + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete_all() attributes to clean up after test (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + free(value_buf); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + +/**@brief + * + * Test to create ~500 kvs. The amount of data store in the cfstore is as follows: + * - total = (220*500)+(256/64)*500*501/2 500*8 = 8236000 = 8.236M + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_create_test_05_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + uint32_t i = 0; + uint32_t bytes_stored = 0; + const uint32_t max_num_kvs_create = 200; + const size_t kv_name_tag_len = 3; + const size_t kv_name_min_len = 10; + const size_t kv_value_min_len = CFSTORE_TEST_BYTE_DATA_TABLE_SIZE; + const size_t max_value_buf_size = kv_value_min_len/64 * (max_num_kvs_create+1); + char kv_name_tag_buf[kv_name_tag_len+1]; + char* value_buf = NULL; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + value_buf = (char*) malloc(max_value_buf_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: out of memory.\n", __func__); + TEST_ASSERT_MESSAGE(value_buf != NULL, cfstore_create_utest_msg_g); + + for(i = 0; i < max_num_kvs_create; i++) + { + memset(value_buf, 0, max_value_buf_size); + snprintf(kv_name_tag_buf, kv_name_tag_len+1, "%0d", (int) i); + ret = cfstore_create_kv_create(kv_name_min_len, kv_name_tag_buf, value_buf, kv_value_min_len/64 * (i+1)); + bytes_stored += kv_name_min_len + i + strlen(kv_name_tag_buf); /* kv_name */ + bytes_stored += kv_value_min_len/64 * (i+1); /* kv value blob */ + bytes_stored += 8; /* kv overhead */ + if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ + CFSTORE_ERRLOG("Out of memory on %d-th KV, trying to allocate memory totalling %d.\n", (int) i, (int) bytes_stored); + break; + } + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("Successfully stored %d-th KV bytes, totalling %d.\n", (int) i, (int) bytes_stored); + } + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete_all() attributes to clean up after test.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + free(value_buf); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +/* structure to encode test data */ +typedef struct cfstore_create_key_name_validate_t { + const char* key_name; + uint32_t f_allowed : 1; +} cfstore_create_key_name_validate_t; + +/* data table encoding test data */ +cfstore_create_key_name_validate_t cfstore_create_test_06_data[] = { + /* ruler for measuring text strings */ + /* 1 1 1 1 1 1 1 1 1 1 2 2 2 */ + /* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ + { "", false}, + { "abc.{1}.efg", true }, + { "abc.{1.efg", false }, + { "abc.1}.efg", false }, + { "abc.{{1}}.efg", false }, + { "abc.{}.efg", true }, + { "abc.}1{.efg", false }, + { ".piety.demands.us.to.honour.truth.above.our.friends", false }, + { "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.{100000000}", false }, + { NULL, false}, +}; +/// @endcond + + +/**@brief + * + * Test whether a key name can be created or not. + * + * @param key_name + * name of the key to create in the store + * @param should_create + * if true, then create KV should succeed, otherwise should fail. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +bool cfstore_create_key_name_validate(const char *key_name, bool should_create) +{ + bool bret = false; + char* test_data = (char*) "test_data"; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + + /* create */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(test_data); + ret = cfstore_test_create((const char*) key_name, test_data, &len, &kdesc); + /* dont not use any functions that require finding the created item as they may not work, + * depending on the construction of the test key_name & match strings */ + if(should_create == true) + { + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to create kv (key_name=%s.\r\n", __func__, key_name); + return bret; + } + CFSTORE_DBGLOG("%s:Success: Create() behaved as expected.\r\n", __func__); + /* delete using the actual name */ + ret = cfstore_test_delete((const char*) key_name); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to delete kv (key_name=%s)(ret=%d).\r\n", __func__, key_name, (int)ret); + } + bret = true; + } + else + { + if(ret >= ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: created kv (key_name=%s) when Create() should have failed.\r\n", __func__, key_name); + return bret; + } + /* the operation failed which was the expected result hence return true*/ + bret = true; + } + return bret; +} + +/** @brief + * + * Test that key names with non-matching braces etc do no get created. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_create_test_06_end(const size_t call_count) +{ + bool ret = false; + int32_t ret32 = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_create_key_name_validate_t* node = cfstore_create_test_06_data; + + (void) call_count; + + while(node->key_name != NULL) + { + ret = cfstore_create_key_name_validate(node->key_name, node->f_allowed); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: test failed (ret=%d, key_name=%s, f_allowed=%d)\n", __func__, (int) ret, node->key_name, (int) node->f_allowed); + TEST_ASSERT_MESSAGE(ret == true, cfstore_create_utest_msg_g); + node++; + } + + ret32 = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret32 >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(CFSTORE_CREATE_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("CREATE_test_00", cfstore_create_test_00), + Case("CREATE_test_01_start", cfstore_utest_default_start), + Case("CREATE_test_01_end", cfstore_create_test_01_end), + Case("CREATE_test_02_start", cfstore_utest_default_start), + Case("CREATE_test_02_end", cfstore_create_test_02_end), + Case("CREATE_test_03_start", cfstore_utest_default_start), + Case("CREATE_test_03_end", cfstore_create_test_03_end), + Case("CREATE_test_04_start", cfstore_utest_default_start), + Case("CREATE_test_04_end", cfstore_create_test_04_end), + Case("CREATE_test_05_start", cfstore_utest_default_start), + Case("CREATE_test_05_end", cfstore_create_test_05_end), + Case("CREATE_test_06_start", cfstore_utest_default_start), + Case("CREATE_test_06_end", cfstore_create_test_06_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/dump/dump.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/dump/dump.cpp new file mode 100644 index 0000000000..48d0074d4b --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/dump/dump.cpp @@ -0,0 +1,86 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Simple tool (implemented as a test case) to dump cfstore contents. + */ + +/** @file dump.cpp test binary for dumping CFSTORE configuration. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include +#include + +using namespace utest::v1; + + +control_t cfstore_dump_test_01_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + ret = cfstore_test_dump(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_LOG("Error: failed to dump CFSTORE contents%s", "\n"); + } + ret = drv->Uninitialize(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_LOG("Error: failed to Uninitialize() CFSTORE%s", "\n"); + } + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(400, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("DUMP_test_01_start", cfstore_utest_default_start), + Case("DUMP_test_01_end", cfstore_dump_test_01_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example1/example1.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example1/example1.cpp new file mode 100644 index 0000000000..6b017b3fcc --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example1/example1.cpp @@ -0,0 +1,935 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** @file example1.cpp Test case to demonstrates each API function works correctly. + * + * \par Example 1 Notes + * + * The example test does the following CFSTORE operations: + * - initialises + * - creates a key-value pair (KV). + * - writes the data for the KV + * - closes KV. + * - flushes the KV to flash + * - opens KV for reading. + * - reads KV and checks the value blob was the same as previously written. + * - closes KV. + * - finds a KV (there is only 1 to find). + * - for the KV returned, get the key name. + * - for the KV returned, get the value length. + * - for the KV returned, delete the KV. + * - find another KV (which fails as there are no more keys to find). + * - flushes the updated state to flash to store the removal of the deleted KV. + * - uninitialises + * - stops + * + * This test is coded so as to work in the following modes: + * - flash sync mode i.e. with caps.asynchronous_ops == false + * - flash async mode i.e. with caps.asynchronous_ops == true + * + * The dual async/sync mode support with the same code is more complicated + * than if the implementation just supported sync mode for example. However, + * it has the benefit of being more versatile. + * + * The test leaves the flash in the same state as at the beginning of the test so + * it can be run a second time on the device without flashing, and the test should + * still work. + * + * \par How to Build Example1 as a Stand-alone Application + * + * This example can be build as a stand-alone application as follows: + * - Create a new mbed application using the `mbed new .` command. + * - Copy this file example1.cpp from the to the top level application directory and rename the file to main.cpp. + * - Build the application with `mbed compile -v -m -t -DCFSTORE_EXAMPLE1_APP` e.g. `mbed compile -v -m K64F -t GCC_ARM -DCFSTORE_EXAMPLE1_APP`. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + +#include "mbed.h" +#include "cfstore_config.h" +#include "Driver_Common.h" +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +static control_t cfstore_example1_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE1_test_00", cfstore_example1_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + +#else + + +#include "mbed.h" + +#ifndef CFSTORE_EXAMPLE1_APP +/* when built as Configuration-Store example, include greentea support otherwise omit */ +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#else // CFSTORE_EXAMPLE1_APP +// map utest types for building as stand alone example +#define control_t void +#define CaseNext +#endif // CFSTORE_EXAMPLE1_APP + +#include "cfstore_config.h" +#include "configuration_store.h" + +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED +#include "flash_journal_strategy_sequential.h" +#include "flash_journal.h" +#include "Driver_Common.h" +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + +#include +#include +#include + +/// @cond CFSTORE_DOXYGEN_DISABLE +#ifndef CFSTORE_EXAMPLE1_APP +using namespace utest::v1; +#endif + + +#define CFSTORE_EX1_TEST_ASSERT(Expr) if (!(Expr)) { printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX1_TEST_ASSERT_EQUAL(expected, actual) if ((expected) != (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX1_TEST_ASSERT_NOT_EQUAL(expected, actual) if ((expected) == (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} + +#define CFSTORE_EX1_TEST_ASSERT_MSG(Expr, _fmt, ...) \ + do \ + { \ + if (!(Expr)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + while (1) ; \ + } \ + }while(0); + +#define CFSTORE_EX1_LOG(_fmt, ...) \ + do \ + { \ + printf(_fmt, __VA_ARGS__); \ + }while(0); + + +const char* cfstore_ex_opcode_str[] = +{ + "UNDEFINED", + "CFSTORE_OPCODE_CLOSE", + "CFSTORE_OPCODE_CREATE", + "CFSTORE_OPCODE_DELETE", + "CFSTORE_OPCODE_FIND", + "CFSTORE_OPCODE_FLUSH", + "CFSTORE_OPCODE_GET_KEY_NAME", + "CFSTORE_OPCODE_GET_STATUS", + "CFSTORE_OPCODE_GET_VALUE_LEN", + "CFSTORE_OPCODE_INITIALIZE", + "CFSTORE_OPCODE_OPEN", + "CFSTORE_OPCODE_POWER_CONTROL", + "CFSTORE_OPCODE_READ", + "CFSTORE_OPCODE_RSEEK", + "CFSTORE_OPCODE_UNINITIALIZE", + "CFSTORE_OPCODE_WRITE", + "CFSTORE_OPCODE_MAX" +}; + +bool cfstore_example1_done = false; +const char* cfstore_ex_kv_name = "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.well.well"; +const char* cfstore_ex_kv_value = "TheRollingStone"; +#define CFSTORE_EX1_RSEEK_OFFSET 10 /* offset to S of Stone */ + +typedef enum cfstore_ex_state_t { + CFSTORE_EX_STATE_INITIALIZING = 1, + CFSTORE_EX_STATE_INIT_DONE, + CFSTORE_EX_STATE_CREATING, + CFSTORE_EX_STATE_CREATE_DONE, + CFSTORE_EX_STATE_WRITING, + CFSTORE_EX_STATE_WRITE_DONE, + CFSTORE_EX_STATE_CLOSING1, + CFSTORE_EX_STATE_CLOSE_DONE1, + CFSTORE_EX_STATE_FLUSHING1, + CFSTORE_EX_STATE_FLUSH_DONE1, + CFSTORE_EX_STATE_OPENING, + CFSTORE_EX_STATE_OPEN_DONE, + CFSTORE_EX_STATE_READING1, + CFSTORE_EX_STATE_READ_DONE1, + CFSTORE_EX_STATE_RSEEKING, + CFSTORE_EX_STATE_RSEEK_DONE, + CFSTORE_EX_STATE_READING2, + CFSTORE_EX_STATE_READ_DONE2, + CFSTORE_EX_STATE_CLOSING2, + CFSTORE_EX_STATE_CLOSE_DONE2, + CFSTORE_EX_STATE_FINDING1, + CFSTORE_EX_STATE_FIND_DONE1, + CFSTORE_EX_STATE_GETTING_KEY_NAME, + CFSTORE_EX_STATE_GET_KEY_NAME_DONE, + CFSTORE_EX_STATE_GETTING_VALUE_LEN, + CFSTORE_EX_STATE_GET_VALUE_LEN_DONE, + CFSTORE_EX_STATE_DELETING, + CFSTORE_EX_STATE_DELETE_DONE, + CFSTORE_EX_STATE_FINDING2, + CFSTORE_EX_STATE_FIND_DONE2, + CFSTORE_EX_STATE_FLUSHING2, + CFSTORE_EX_STATE_FLUSH_DONE2, + CFSTORE_EX_STATE_UNINITIALIZING, + CFSTORE_EX_STATE_UNINIT_DONE +} cfstore_ex_state_t; + + +typedef struct cfstore_example1_ctx_t +{ + ARM_CFSTORE_CAPABILITIES caps; + uint8_t hkey[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_next_buf[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_prev_buf[CFSTORE_HANDLE_BUFSIZE]; + ARM_CFSTORE_HANDLE hkey_next; + ARM_CFSTORE_HANDLE hkey_prev; + cfstore_ex_state_t state; + ARM_CFSTORE_SIZE len; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + /* callback attributes*/ + int32_t callback_status; + ARM_CFSTORE_HANDLE callback_handle; +} cfstore_example1_ctx_t; + +static cfstore_example1_ctx_t cfstore_example1_ctx_g; + +extern ARM_CFSTORE_DRIVER cfstore_driver; +ARM_CFSTORE_DRIVER *cfstore_drv = &cfstore_driver; + +/* forward declarations */ +static void cfstore_ex_fms_update(cfstore_example1_ctx_t* ctx); +/// @endcond + + +/* @brief test startup code to reset flash + */ +static int32_t cfstore_test_startup(void) +{ + ARM_CFSTORE_CAPABILITIES caps = cfstore_driver.GetCapabilities(); + CFSTORE_EX1_LOG("INITIALIZING: caps.asynchronous_ops=%d\n", (int) caps.asynchronous_ops); + +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED + + int32_t ret = ARM_DRIVER_ERROR; + static FlashJournal_t jrnl; + extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); + const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); + + ret = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (ret=%d)\r\n", __func__, (int) ret); + + ret = FlashJournal_reset(&jrnl); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_reset() failed (ret=%d)\r\n", __func__, (int) ret); +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + + return ARM_DRIVER_OK; +} + + +static void cfstore_ex_callback(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle) +{ + (void) handle; + + cfstore_example1_ctx_t* ctx = (cfstore_example1_ctx_t*) client_context; + + /* CFSTORE_EX1_LOG("%s:entered: status=%d, cmd_code=%d (%s) handle=%p\r\n", __func__, (int) status, (int) cmd_code, cfstore_ex_opcode_str[cmd_code], handle); */ + switch(cmd_code) + { + case CFSTORE_OPCODE_INITIALIZE: + ctx->state = CFSTORE_EX_STATE_INIT_DONE; + break; + case CFSTORE_OPCODE_CREATE: + ctx->state = CFSTORE_EX_STATE_CREATE_DONE; + break; + case CFSTORE_OPCODE_WRITE: + ctx->state = CFSTORE_EX_STATE_WRITE_DONE; + break; + case CFSTORE_OPCODE_CLOSE: + switch(ctx->state) + { + case(CFSTORE_EX_STATE_CLOSING1): + ctx->state = CFSTORE_EX_STATE_CLOSE_DONE1; + break; + case(CFSTORE_EX_STATE_CLOSING2): + ctx->state = CFSTORE_EX_STATE_CLOSE_DONE2; + break; + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown Close() error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + } + break; + case CFSTORE_OPCODE_FLUSH: + switch(ctx->state) + { + case(CFSTORE_EX_STATE_FLUSHING1): + ctx->state = CFSTORE_EX_STATE_FLUSH_DONE1; + break; + case(CFSTORE_EX_STATE_FLUSHING2): + ctx->state = CFSTORE_EX_STATE_FLUSH_DONE2; + break; + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown Find() error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + } + break; + case CFSTORE_OPCODE_OPEN: + ctx->state = CFSTORE_EX_STATE_OPEN_DONE; + break; + case CFSTORE_OPCODE_FIND: + switch(ctx->state) + { + case(CFSTORE_EX_STATE_FINDING1): + ctx->state = CFSTORE_EX_STATE_FIND_DONE1; + break; + case(CFSTORE_EX_STATE_FINDING2): + ctx->state = CFSTORE_EX_STATE_FIND_DONE2; + break; + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown Find() error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + } + break; + case CFSTORE_OPCODE_GET_KEY_NAME: + ctx->state = CFSTORE_EX_STATE_GET_KEY_NAME_DONE; + break; + case CFSTORE_OPCODE_GET_VALUE_LEN: + ctx->state = CFSTORE_EX_STATE_GET_VALUE_LEN_DONE; + break; + case CFSTORE_OPCODE_READ: + switch(ctx->state) + { + case(CFSTORE_EX_STATE_READING1): + ctx->state = CFSTORE_EX_STATE_READ_DONE1; + break; + case(CFSTORE_EX_STATE_READING2): + ctx->state = CFSTORE_EX_STATE_READ_DONE2; + break; + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown Read() error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + } + break; + case CFSTORE_OPCODE_RSEEK: + ctx->state = CFSTORE_EX_STATE_RSEEK_DONE; + break; + case CFSTORE_OPCODE_DELETE: + ctx->state = CFSTORE_EX_STATE_DELETE_DONE; + break; + case CFSTORE_OPCODE_UNINITIALIZE: + ctx->state = CFSTORE_EX_STATE_UNINIT_DONE; + break; + case CFSTORE_OPCODE_GET_STATUS: + case CFSTORE_OPCODE_POWER_CONTROL: + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: received asynchronous notification for opcode=%d (%s)\r\b", __func__, cmd_code, cmd_code < CFSTORE_OPCODE_MAX ? cfstore_ex_opcode_str[cmd_code] : "unknown"); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + } + ctx->callback_status = status; + ctx->callback_handle = handle; + cfstore_ex_fms_update(ctx); + return; +} + +static void cfstore_ex_fms_update(cfstore_example1_ctx_t* ctx) +{ + int32_t ret; + + switch (ctx->state) + { + case CFSTORE_EX_STATE_INITIALIZING: + CFSTORE_EX1_LOG("INITIALIZING%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_INITIALIZE can be invoked before Initialize() has returned + ret = cfstore_drv->Initialize(cfstore_ex_callback, ctx); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_INIT_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_INIT_DONE: + CFSTORE_EX1_LOG("INIT_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Initialize() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_INITIALIZE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_CREATING; + // intentional fall-through + + case CFSTORE_EX_STATE_CREATING: + CFSTORE_EX1_LOG("CREATING%s", "\r\n"); + memset(&ctx->kdesc, 0, sizeof(ARM_CFSTORE_KEYDESC)); + ctx->kdesc.drl = ARM_RETENTION_NVM; + ctx->len = strlen(cfstore_ex_kv_value); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_CREATE can be invoked before Create() has returned + ret = cfstore_drv->Create(cfstore_ex_kv_name, ctx->len, &ctx->kdesc, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Create() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_CREATE_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_CREATE_DONE: + CFSTORE_EX1_LOG("CREATE_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Create() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_CREATE) received handle (%p) is not the hkey supplied to Create()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + ctx->state = CFSTORE_EX_STATE_WRITING; + // intentional fall-through + + case CFSTORE_EX_STATE_WRITING: + CFSTORE_EX1_LOG("WRITING%s", "\r\n"); + ctx->len = strlen(cfstore_ex_kv_value); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_WRITE can be invoked before Write() has returned + ret = cfstore_drv->Write(ctx->hkey, cfstore_ex_kv_value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Write() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_WRITE_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_WRITE_DONE: + CFSTORE_EX1_LOG("WRITE_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Write() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) strlen(cfstore_ex_kv_value), "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ctx->callback_status, (int32_t) strlen(cfstore_ex_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) ctx->len, "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ctx->callback_status, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_WRITE) received handle (%p) is not the hkey supplied to Write()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + ctx->state = CFSTORE_EX_STATE_CLOSING1; + // intentional fall-through + + case CFSTORE_EX_STATE_CLOSING1: + CFSTORE_EX1_LOG("CLOSING1%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_CLOSE can be invoked before Close() has returned + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_CLOSE_DONE1; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_CLOSE_DONE1: + CFSTORE_EX1_LOG("CLOSE_DONE1%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Close() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_CLOSE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_FLUSHING1; + // intentional fall-through + + case CFSTORE_EX_STATE_FLUSHING1: + CFSTORE_EX1_LOG("FLUSHING1%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_FLUSH can be invoked before Flush() has returned + ret = cfstore_drv->Flush(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_FLUSH_DONE1; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_FLUSH_DONE1: + CFSTORE_EX1_LOG("FLUSH_DONE1%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Flush() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_FLUSH) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_OPENING; + // intentional fall-through + + case CFSTORE_EX_STATE_OPENING: + CFSTORE_EX1_LOG("OPENING%s", "\r\n"); + memset(&ctx->flags, 0, sizeof(ctx->flags)); + memset(&ctx->hkey, 0, CFSTORE_HANDLE_BUFSIZE); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_OPEN can be invoked before Open() has returned + ret = cfstore_drv->Open(cfstore_ex_kv_name, ctx->flags, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Open() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_OPEN_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_OPEN_DONE: + CFSTORE_EX1_LOG("OPEN_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Open() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_OPEN) received handle (%p) is not the hkey supplied to Open()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + ctx->state = CFSTORE_EX_STATE_READING1; + // intentional fall-through + + case CFSTORE_EX_STATE_READING1: + CFSTORE_EX1_LOG("READING1%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_READ can be invoked before Read() has returned + ret = cfstore_drv->Read(ctx->hkey, ctx->value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Read() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_READ_DONE1; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_READ_DONE1: + CFSTORE_EX1_LOG("READ_DONE1%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Read() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) strlen(cfstore_ex_kv_value), "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ctx->callback_status, (int32_t) strlen(cfstore_ex_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) ctx->len, "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ctx->callback_status, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_READ) received handle (%p) is not the hkey supplied to Read()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, cfstore_ex_kv_value, strlen(cfstore_ex_kv_value)) == 0, "%s:Error: the read value (%s) is not as expected (%s)\r\n", __func__, ctx->value, cfstore_ex_kv_value); + ctx->state = CFSTORE_EX_STATE_RSEEKING; + // intentional fall-through + + case CFSTORE_EX_STATE_RSEEKING: + CFSTORE_EX1_LOG("RSEEKING%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_READ can be invoked before Read() has returned + ret = cfstore_drv->Rseek(ctx->hkey, CFSTORE_EX1_RSEEK_OFFSET); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Rseek() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_RSEEK_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_RSEEK_DONE: + CFSTORE_EX1_LOG("RSEEK_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Read() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_RSEEK) received handle (%p) is not the hkey supplied to Read()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + ctx->state = CFSTORE_EX_STATE_READING2; + // intentional fall-through + + case CFSTORE_EX_STATE_READING2: + CFSTORE_EX1_LOG("READING2%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_READ can be invoked before Read() has returned + ret = cfstore_drv->Read(ctx->hkey, ctx->value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Read() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_READ_DONE2; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_READ_DONE2: + CFSTORE_EX1_LOG("READ_DONE2%s", "\r\n"); + CFSTORE_EX1_LOG("%s: value=%s\r\n", __func__, ctx->value); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Read() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) strlen(&cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET]), "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ctx->callback_status, (int32_t) strlen(&cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET])); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) ctx->len, "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ctx->callback_status, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_READ) received handle (%p) is not the hkey supplied to Read()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, &cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET], strlen(&cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET])) == 0, "%s:Error: the read value (%s) is not as expected (%s)\r\n", __func__, ctx->value, &cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET]); + ctx->state = CFSTORE_EX_STATE_CLOSING2; + // intentional fall-through + + case CFSTORE_EX_STATE_CLOSING2: + CFSTORE_EX1_LOG("CLOSING2%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_CLOSE can be invoked before Close() has returned + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_CLOSE_DONE2; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_CLOSE_DONE2: + CFSTORE_EX1_LOG("CLOSE_DONE2%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Close() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_CLOSE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_FINDING1; + // intentional fall-through + + case CFSTORE_EX_STATE_FINDING1: + CFSTORE_EX1_LOG("FINDING1%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_FIND can be invoked before Find() has returned + ret = cfstore_drv->Find("*", ctx->hkey_next, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Find() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_FIND_DONE1; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_FIND_DONE1: + CFSTORE_EX1_LOG("FIND_DONE1%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == ARM_DRIVER_OK, "%s:Error: Find() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey_prev, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_FIND) received handle (%p) is not the hkey supplied to Find()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey_prev); + ctx->state = CFSTORE_EX_STATE_GETTING_KEY_NAME; + // intentional fall-through + + case CFSTORE_EX_STATE_GETTING_KEY_NAME: + CFSTORE_EX1_LOG("GETTING_KEY_NAME%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_GET_KEY_NAME can be invoked before GetKeyName() has returned + ret = cfstore_drv->GetKeyName(ctx->hkey_prev, ctx->value, (uint8_t*) &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: GetKeyName() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_GET_KEY_NAME_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_GET_KEY_NAME_DONE: + CFSTORE_EX1_LOG("GET_KEY_NAME_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: GetKeyName() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG( ((int32_t) ctx->len == ((int32_t) strlen(cfstore_ex_kv_name)+1)), "%s:Error: GetKeyName() updated value of len parameter (%ld) != strlen(cfstore_ex_kv_name) (%ld) (\r\n", __func__, (int32_t) ctx->len, (int32_t) strlen(cfstore_ex_kv_name)); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey_prev, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_GET_KEY_NAME) received handle (%p) is not the hkey supplied to GetKeyName()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, cfstore_ex_kv_name, strlen(cfstore_ex_kv_name)) == 0, "%s:Error: the key name (%s) is not as expected (%s)\r\n", __func__, ctx->value, cfstore_ex_kv_name); + ctx->state = CFSTORE_EX_STATE_GETTING_VALUE_LEN; + // intentional fall-through + + case CFSTORE_EX_STATE_GETTING_VALUE_LEN: + CFSTORE_EX1_LOG("GETTING_VALUE_LEN%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_GET_VALUE_LEN can be invoked before GetValueLen() has returned + ret = cfstore_drv->GetValueLen(ctx->hkey_prev, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: GetValueLen() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_GET_VALUE_LEN_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_GET_VALUE_LEN_DONE: + CFSTORE_EX1_LOG("GET_VALUE_LEN_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: GetValueLen() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG((int32_t) ctx->len == (int32_t) strlen(cfstore_ex_kv_value), "%s:Error: GetValueLen() updated value of len parameter (%ld) != strlen(cfstore_ex_kv_value)(%ld) \r\n", __func__, (int32_t) ctx->len, (int32_t) strlen(cfstore_ex_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey_prev, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_GET_VALUE_LEN) received handle (%p) is not the hkey supplied to GetValueLen()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey_prev); + ctx->state = CFSTORE_EX_STATE_DELETING; + // intentional fall-through + + case CFSTORE_EX_STATE_DELETING: + CFSTORE_EX1_LOG("DELETING%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_DELETE can be invoked before Delete() has returned + ret = cfstore_drv->Delete(ctx->callback_handle); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_DELETE_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_DELETE_DONE: + CFSTORE_EX1_LOG("DELETE_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Delete() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_DELETE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + CFSTORE_HANDLE_SWAP(ctx->hkey_prev, ctx->hkey_next); + ctx->state = CFSTORE_EX_STATE_FINDING2; + // intentional fall-through + + case CFSTORE_EX_STATE_FINDING2: + CFSTORE_EX1_LOG("FINDING2%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_FIND can be invoked before Find() has returned + ret = cfstore_drv->Find("*", ctx->hkey_next, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, "%s:Error: Find() failed to return expected value of ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND (ret=%ld)\r\n", __func__, ret); + if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_FIND_DONE2; + break; + } else if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_FIND_DONE2: + CFSTORE_EX1_LOG("FIND_DONE2%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, "%s:Error: Find() completion should have been ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND (status=%ld)\r\n", __func__, ctx->callback_status); + ctx->state = CFSTORE_EX_STATE_FLUSHING2; + // intentional fall-through + + case CFSTORE_EX_STATE_FLUSHING2: + CFSTORE_EX1_LOG("FLUSHING2%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_FLUSH can be invoked before Flush() has returned + ret = cfstore_drv->Flush(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error:2: Flush() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_FLUSH_DONE2; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_FLUSH_DONE2: + CFSTORE_EX1_LOG("FLUSH_DONE2%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Flush() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_FLUSH) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_UNINITIALIZING; + // intentional fall-through + + case CFSTORE_EX_STATE_UNINITIALIZING: + CFSTORE_EX1_LOG("UNINITIALIZING%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Uninitialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_UNINIT_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + /* The following 'break' statement generates ARMCC #111-D: statement is unreachable warning + * and hence is commented out. Re-instate if previous assert is removed. + * break; */ + + case CFSTORE_EX_STATE_UNINIT_DONE: + CFSTORE_EX1_LOG("UNINIT_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_UNINITIALIZE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + cfstore_example1_done = true; + CFSTORE_EX1_LOG("***************%s", "\r\n"); + CFSTORE_EX1_LOG("*** SUCCESS ***%s", "\r\n"); + CFSTORE_EX1_LOG("***************%s", "\r\n"); + break; + } +} + +static control_t cfstore_example1_app_start(const size_t call_count) +{ + cfstore_example1_ctx_t* ctx = &cfstore_example1_ctx_g; + + (void) call_count; + + /* initialise the context */ + memset(ctx, 0, sizeof(cfstore_example1_ctx_t)); + cfstore_example1_done = false; + ctx->hkey_next = ctx->hkey_next_buf; + ctx->hkey_prev = ctx->hkey_prev_buf; + ctx->callback_status = ARM_DRIVER_ERROR; + ctx->state = CFSTORE_EX_STATE_INITIALIZING; + ctx->caps = cfstore_drv->GetCapabilities(); + cfstore_ex_fms_update(ctx); + + /* main application worker loop */ + while (!cfstore_example1_done) + { + // do some work + CFSTORE_EX1_LOG("%s: going to sleep!\r\n", __func__); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 + __WFE(); +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 + /* mbedosV3++ + * todo: port __WFE() + */ +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + CFSTORE_EX1_LOG("%s: woke up!\r\n", __func__); + } + return CaseNext; +} + +#ifndef CFSTORE_EXAMPLE1_APP +/* when built as Configuration-Store example, include greentea support otherwise omit */ + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_example1_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + (void) call_count; + + ret = cfstore_test_startup(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(25, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE1_test_00", cfstore_example1_test_00), + Case("EXAMPLE1_test_01_start", cfstore_example1_app_start), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + + +#else // CFSTORE_EXAMPLE1_APP + +// stand alone Configuration-Store-Example +void app_start(int argc __unused, char** argv __unused) +{ + cfstore_example1_app_start(0); +} + + + +#endif // CFSTORE_EXAMPLE1_APP + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example2/example2.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example2/example2.cpp new file mode 100644 index 0000000000..978d0340c3 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example2/example2.cpp @@ -0,0 +1,259 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** @file example2.cpp Test case to demonstrate a subset of the API functions each work correctly. + * + * Overview of test: + * - initialises cfstore + * - creates a key called "com.arm.mbed.spv.assets.asset2.payload" with value blob length = 15 = strlen("Grumpy old man")+1. + * - writes the data for the key to be "Grumpy old man" + * - closes kv. + * - opens kv for reading/writing + * - reads the value blob and checks its == "Grumpy old man" + * - writes the first 11 chars of the value blob to be "Grumpy man" plus a NULL; + * - reads the value blob back and checks its as expected + * + * This test is coded so as to work in the following modes: + * - flash sync mode i.e. with caps.asynchronous_ops == false + * - flash async mode i.e. with caps.asynchronous_ops == true + */ +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + +#include +#include +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_example2_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* defines */ +/// @cond CFSTORE_DOXYGEN_DISABLE +#define PvMemSet memset +#define PvStrLen strlen +/// @endcond + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_example2_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_example2_utest_msg_g); + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +ARM_CFSTORE_DRIVER *drv = &cfstore_driver; +/// @endcond + + +static int32_t CreateKeyValueStore( + const char *keyName, + const char *data, + ARM_CFSTORE_SIZE *dataLength, + ARM_CFSTORE_KEYDESC *keyDesc) +{ + int32_t cfsStatus = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE valueLength = 0; + ARM_CFSTORE_HANDLE_INIT(hkey); + + valueLength = *dataLength; + cfsStatus = drv->Create(keyName, valueLength, keyDesc, hkey); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, cfsStatus); + + valueLength = *dataLength; + cfsStatus = drv->Write(hkey, data, &valueLength); + /* + * (1) Note the following: + * - if cfsStatus > 0 then Write() has completed synchronously and returned the number of bytes written (irrespective of the caps.asynchronous_ops attribute). + * - if cfsStatus == ARM_DRIVER_OK then: + * - if caps.asynchronous_ops == true then the operation will be completed with registered client callback passed to Initialize(). + * - if caps.asynchronous_ops == false then the operation has completed synchronously and no bytes were written. + * - if cfsStatus < ARM_DRIVER_OK then an error has occurred + */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (rc=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example2_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: valueLength(%d) does not match the expected dataLength(%d)\n", __func__, (int) valueLength, (int) *dataLength); + TEST_ASSERT_MESSAGE(*dataLength == valueLength, cfstore_example2_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: Write() return value cfsStatus(%d) does not match the expected dataLength(%d)\n", __func__, (int) cfsStatus, (int) *dataLength); + TEST_ASSERT_MESSAGE((int32_t) *dataLength == cfsStatus, cfstore_example2_utest_msg_g); + + drv->Close(hkey); + /* CreateKeyValueStore() returns what was returned from Write(). See (1) above for description. */ + return cfsStatus; +} + +static control_t cfstore_example2_test_01(const size_t call_count) +{ + int32_t cfsStatus; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_HANDLE_INIT(updatedKeyH); + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_SIZE valueLen; + char* ptr = NULL; + + const char key[] = "com.arm.mbed.spv.assets.asset2.payload"; + const char value[] = "Grumpy old man"; + + (void) call_count; + + // It must not exceed the value_len field specified when the Key-Value pair was created + const char newDataToWrite[] = "Grumpy man"; + + char readBuf[CFSTORE_KEY_NAME_MAX_LENGTH + 1]; + ARM_CFSTORE_SIZE len = 0; + + // Write a key-value pair + + PvMemSet(&kdesc, 0, sizeof(kdesc)); + PvMemSet(&flags, 0, sizeof(flags)); + PvMemSet(readBuf, 0, CFSTORE_KEY_NAME_MAX_LENGTH + 1); + + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + /* The length supplied to Write() is the number of octets to store in the blob. + * Specifying the following value for valueLen will store the terminating null to + * a string, for example. + */ + valueLen = PvStrLen(value) + 1; + + cfsStatus = drv->Initialize(NULL, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Initialize() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example2_utest_msg_g); + + cfsStatus = CreateKeyValueStore(key, value, &valueLen, &kdesc); + + /* CreateKeyValueStore() returns the number of characters written, which can vary between 0 and the supplied arg valueLen + * - in the case that this example is compiled for flash mode sync, CreateKeyValueStore(), on success should always return valueLen + * - in the case that this example is compiled for flash mode async, CreateKeyValueStore() on success may return a value 0 to valueLen + * with async notification of the completed transaction. + */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: valueLen(%d) does not match the expected returned value from CreateKeyValueStore(%d)\n", __func__, (int) valueLen, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus == (int32_t) valueLen, cfstore_example2_utest_msg_g); + + // Read key-value pair with 'Write' permission + + flags.read = true; + flags.write = true; + cfsStatus = drv->Open(key, flags, hkey); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, cfsStatus); + + len = sizeof(readBuf); + cfsStatus = drv->Read(hkey, readBuf, &len); + /* Read() returns the number of characters read, which can vary between 0 and the size of the value blob, and the size of the supplied buffer */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned value (%d) does not match the created length of the value blob(%d)\n", __func__, (int) cfsStatus, (int) PvStrLen(value) + 1); + TEST_ASSERT_MESSAGE(cfsStatus == (int32_t) (PvStrLen(value) + 1), cfstore_example2_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned len value (%d) does not match the created length of the value blob(%d)\n", __func__, (int) len, (int) PvStrLen(value) + 1); + TEST_ASSERT_MESSAGE(len == PvStrLen(value) + 1, cfstore_example2_utest_msg_g); + + /* Note: + * - original data = "Grumpy old man", which is 14+1 chars inc NULL + * - New data = "Grumpy man" which is 10+1 chars inc NULL + * - when the key "com.arm.mbed.spv.assets.asset2.payload"; was created, it was created with a value blob size of 14+1=15 chars. + * - The new data is shorter that the old data so it will be accommodated in the value blob + * - The size of the value blob will stay the same. + */ + // Update the value and value length + /* note len set to sizeof(newDataToWrite) which includes the terminating null of the string */ + len = sizeof(newDataToWrite); + cfsStatus = drv->Write(hkey, newDataToWrite, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Write() returned cfsStatus value (%d) does not match the length of new data written(%d)\n", __func__, (int) cfsStatus, (int) sizeof(newDataToWrite)); + TEST_ASSERT_MESSAGE(cfsStatus == (int32_t) sizeof(newDataToWrite), cfstore_example2_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Write() returned len (%d) does not match the length of new data written(%d)\n", __func__, (int) len, (int) sizeof(newDataToWrite)); + TEST_ASSERT_MESSAGE((int32_t) len == (int32_t) sizeof(newDataToWrite), cfstore_example2_utest_msg_g); + + drv->Close(hkey); + + // Check that the value was updated + flags.write = false; + cfsStatus = drv->Open(key, flags, updatedKeyH); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, cfsStatus); + + len = CFSTORE_KEY_NAME_MAX_LENGTH; + PvMemSet(readBuf, 0, len); + cfsStatus = drv->Read(updatedKeyH, readBuf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned value (%d) does not match the created length of the value blob(%d)\n", __func__, (int) cfsStatus, (int) PvStrLen(value) + 1); + TEST_ASSERT_MESSAGE(cfsStatus == (int32_t) (PvStrLen(value) + 1), cfstore_example2_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned len value (%d) does not match the created length of the value blob(%d)\n", __func__, (int) len, (int) PvStrLen(value) + 1); + TEST_ASSERT_MESSAGE(len == (PvStrLen(value) + 1), cfstore_example2_utest_msg_g); + + /* convert any terminating nulls to '=' */ + while( (ptr = (char*) memchr(readBuf, 0, (PvStrLen(value) + 1))) != NULL) + { + *ptr = '='; + } + /* check the data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned unexpected string (%s) where nulls have been converted to '=' chars\n", __func__, readBuf); + TEST_ASSERT_MESSAGE(strncmp(readBuf, "Grumpy man=man=", (PvStrLen(value) + 1)) == 0, cfstore_example2_utest_msg_g); + + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("Success: New value of KV (%s) value blob (with nulls converted to '=') = (%s)\n", key, readBuf); + + drv->Close(updatedKeyH); + + cfsStatus = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example2_utest_msg_g); + + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(400, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE2_test_00", cfstore_example2_test_00), + Case("EXAMPLE2_test_01", cfstore_example2_test_01), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example3/example3.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example3/example3.cpp new file mode 100644 index 0000000000..5373d52f4e --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example3/example3.cpp @@ -0,0 +1,329 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** @file example3.cpp Test case to demonstrate each API function works correctly. + * + * \par Example 3 Notes + * + * Example3 is a synchronous mode example for creating key-values in the persistent storage. + * + * The flash-journal synchronous mode example test does the following CFSTORE operations: + * - initialises + * - creates a key-value pair (KV). + * - writes the data for the KV + * - closes KV. + * - flushes the KV to flash + * - opens KV for reading. + * - reads KV and checks the value blob was the same as previously written. + * - closes KV. + * - finds a KV (there is only 1 to find). + * - for the KV returned, get the key name. + * - for the KV returned, get the value length. + * - for the KV returned, delete the KV. + * - find another KV (which fails as there are no more keys to find). + * - flushes the updated state to flash to store the removal of the deleted KV. + * - uninitialises + * - stops + * + * This test is coded so as to work only in flash journal sync mode + * i.e. with caps.asynchronous_ops == false + * + * The test leaves the flash in the same state as at the beginning of the test so + * it can be run a second time on the device without flashing, and the test should + * still work. + * + * \par How to Build Example3 as a Stand-alone Application + * + * This example can be build as a stand-alone application as follows: + * - Create a new mbed application using the `mbed new .` command. + * - Copy this file example3.cpp from the to the top level application directory and rename the file to main.cpp. + * - Build the application with `mbed compile -v -m -t -DCFSTORE_EXAMPLE3_APP` e.g. `mbed compile -v -m K64F -t GCC_ARM -DCFSTORE_EXAMPLE3_APP`. + * + */ +#include "mbed.h" +#ifndef CFSTORE_EXAMPLE3_APP +/* when built as Configuration-Store example, include greentea support otherwise omit */ +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#else // CFSTORE_EXAMPLE3_APP +/* map utest types for building as stand alone example */ +#define control_t void +#define CaseNext +#endif // CFSTORE_EXAMPLE3_APP + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "configuration_store.h" + +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + +#include +#include +#include + +#ifndef CFSTORE_EXAMPLE3_APP +using namespace utest::v1; +#endif + + +/// @cond CFSTORE_DOXYGEN_DISABLE +#define CFSTORE_EX1_TEST_ASSERT(Expr) if (!(Expr)) { printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX1_TEST_ASSERT_EQUAL(expected, actual) if ((expected) != (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX1_TEST_ASSERT_NOT_EQUAL(expected, actual) if ((expected) == (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} + +#define CFSTORE_EX1_TEST_ASSERT_MSG(Expr, _fmt, ...) \ + do \ + { \ + if (!(Expr)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + while (1) ; \ + } \ + }while(0); + +#define CFSTORE_EX1_LOG(_fmt, ...) \ + do \ + { \ + printf(_fmt, __VA_ARGS__); \ + }while(0); + + +const char* cfstore_ex3_opcode_str[] = +{ + "UNDEFINED", + "CFSTORE_OPCODE_CLOSE", + "CFSTORE_OPCODE_CREATE", + "CFSTORE_OPCODE_DELETE", + "CFSTORE_OPCODE_FIND", + "CFSTORE_OPCODE_FLUSH", + "CFSTORE_OPCODE_GET_KEY_NAME", + "CFSTORE_OPCODE_GET_STATUS", + "CFSTORE_OPCODE_GET_VALUE_LEN", + "CFSTORE_OPCODE_INITIALIZE", + "CFSTORE_OPCODE_OPEN", + "CFSTORE_OPCODE_POWER_CONTROL", + "CFSTORE_OPCODE_READ", + "CFSTORE_OPCODE_RSEEK", + "CFSTORE_OPCODE_UNINITIALIZE", + "CFSTORE_OPCODE_WRITE", + "CFSTORE_OPCODE_MAX" +}; + +const char* cfstore_ex3_kv_name = "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.well.well"; +const char* cfstore_ex3_kv_value = "TheRollingStone"; +#define CFSTORE_EX1_RSEEK_OFFSET 10 /* offset to S of Stone */ + +typedef struct cfstore_example3_ctx_t +{ + ARM_CFSTORE_CAPABILITIES caps; + uint8_t hkey[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_next_buf[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_prev_buf[CFSTORE_HANDLE_BUFSIZE]; + ARM_CFSTORE_HANDLE hkey_next; + ARM_CFSTORE_HANDLE hkey_prev; + ARM_CFSTORE_SIZE len; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; +} cfstore_example3_ctx_t; + +static cfstore_example3_ctx_t cfstore_example3_ctx_g; + +extern ARM_CFSTORE_DRIVER cfstore_driver; +ARM_CFSTORE_DRIVER *cfstore_drv = &cfstore_driver; +/// @endcond + + +static void cfstore_ex3_test_01(cfstore_example3_ctx_t* ctx) +{ + int32_t ret; + + CFSTORE_EX1_LOG("INITIALIZING%s", "\r\n"); + ret = cfstore_drv->Initialize(NULL, NULL); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("CREATING%s", "\r\n"); + memset(&ctx->kdesc, 0, sizeof(ARM_CFSTORE_KEYDESC)); + ctx->kdesc.drl = ARM_RETENTION_NVM; + ctx->len = strlen(cfstore_ex3_kv_value); + ret = cfstore_drv->Create(cfstore_ex3_kv_name, ctx->len, &ctx->kdesc, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Create() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("WRITING%s", "\r\n"); + ctx->len = strlen(cfstore_ex3_kv_value); + ret = cfstore_drv->Write(ctx->hkey, cfstore_ex3_kv_value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Write() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) strlen(cfstore_ex3_kv_value), "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ret, (int32_t) strlen(cfstore_ex3_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) ctx->len, "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ret, (int32_t) ctx->len); + + CFSTORE_EX1_LOG("CLOSING1%s", "\r\n"); + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("FLUSHING1%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("OPENING%s", "\r\n"); + memset(&ctx->flags, 0, sizeof(ctx->flags)); + memset(&ctx->hkey, 0, CFSTORE_HANDLE_BUFSIZE); + ret = cfstore_drv->Open(cfstore_ex3_kv_name, ctx->flags, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Open() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("READING1%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + ret = cfstore_drv->Read(ctx->hkey, ctx->value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Read() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) strlen(cfstore_ex3_kv_value), "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ret, (int32_t) strlen(cfstore_ex3_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) ctx->len, "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ret, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, cfstore_ex3_kv_value, strlen(cfstore_ex3_kv_value)) == 0, "%s:Error: the read value (%s) is not as expected (%s)\r\n", __func__, ctx->value, cfstore_ex3_kv_value); + + CFSTORE_EX1_LOG("RSEEKING%s", "\r\n"); + ret = cfstore_drv->Rseek(ctx->hkey, CFSTORE_EX1_RSEEK_OFFSET); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Rseek() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("READING2%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + ret = cfstore_drv->Read(ctx->hkey, ctx->value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Read() failed (ret=%ld)\r\n", __func__, ret); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) strlen(&cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET]), "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ret, (int32_t) strlen(&cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET])); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) ctx->len, "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ret, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, &cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET], strlen(&cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET])) == 0, "%s:Error: the read value (%s) is not as expected (%s)\r\n", __func__, ctx->value, &cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET]); + + CFSTORE_EX1_LOG("CLOSING2%s", "\r\n"); + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("FINDING1%s", "\r\n"); + ret = cfstore_drv->Find("*", ctx->hkey_next, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Find() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("GETTING_KEY_NAME%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + ret = cfstore_drv->GetKeyName(ctx->hkey_prev, ctx->value, (uint8_t*) &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: GetKeyName() failed (ret=%ld)\r\n", __func__, ret); + CFSTORE_EX1_TEST_ASSERT_MSG( ((int32_t) ctx->len == ((int32_t) strlen(cfstore_ex3_kv_name)+1)), "%s:Error: GetKeyName() updated value of len parameter (%ld) != strlen(cfstore_ex3_kv_name) (%ld) (\r\n", __func__, (int32_t) ctx->len, (int32_t) strlen(cfstore_ex3_kv_name)); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, cfstore_ex3_kv_name, strlen(cfstore_ex3_kv_name)) == 0, "%s:Error: the key name (%s) is not as expected (%s)\r\n", __func__, ctx->value, cfstore_ex3_kv_name); + + CFSTORE_EX1_LOG("GETTING_VALUE_LEN%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + ret = cfstore_drv->GetValueLen(ctx->hkey_prev, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: GetValueLen() failed (ret=%ld)\r\n", __func__, ret); + CFSTORE_EX1_TEST_ASSERT_MSG((int32_t) ctx->len == (int32_t) strlen(cfstore_ex3_kv_value), "%s:Error: GetValueLen() updated value of len parameter (%ld) != strlen(cfstore_ex3_kv_value)(%ld) \r\n", __func__, (int32_t) ctx->len, (int32_t) strlen(cfstore_ex3_kv_value)); + + CFSTORE_EX1_LOG("DELETING%s", "\r\n"); + ret = cfstore_drv->Delete(ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + CFSTORE_HANDLE_SWAP(ctx->hkey_prev, ctx->hkey_next); + + CFSTORE_EX1_LOG("FINDING2%s", "\r\n"); + ret = cfstore_drv->Find("*", ctx->hkey_next, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, "%s:Error: Find() failed to return expected value of ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("FLUSHING2%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error:2: Flush() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("UNINITIALIZING%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Uninitialize() should return ret >= 0 for synch mode(ret=%ld)\r\n", __func__, ret); + CFSTORE_EX1_LOG("***************%s", "\r\n"); + CFSTORE_EX1_LOG("*** SUCCESS ***%s", "\r\n"); + CFSTORE_EX1_LOG("***************%s", "\r\n"); + return; +} + +static control_t cfstore_example3_app_start(const size_t call_count) +{ + cfstore_example3_ctx_t* ctx = &cfstore_example3_ctx_g; + + (void) call_count; + + /* initialise the context */ + memset(ctx, 0, sizeof(cfstore_example3_ctx_t)); + ctx->hkey_next = ctx->hkey_next_buf; + ctx->hkey_prev = ctx->hkey_prev_buf; + ctx->caps = cfstore_drv->GetCapabilities(); + CFSTORE_EX1_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops); + if(ctx->caps.asynchronous_ops == true){ + /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true + * This means the test will conveniently pass when run in CI as part of async mode testing */ + CFSTORE_EX1_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); + return CaseNext; + } + cfstore_ex3_test_01(ctx); + return CaseNext; +} + +#ifndef CFSTORE_EXAMPLE3_APP +/* when built as Configuration-Store example, include greentea support otherwise omit */ + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_example3_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE3_test_00", cfstore_example3_test_00), + Case("EXAMPLE3_test_01_start", cfstore_example3_app_start), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + + +#else // CFSTORE_EXAMPLE3_APP + +// stand alone Configuration-Store-Example +void app_start(int argc __unused, char** argv __unused) +{ + cfstore_example3_app_start(0); +} + + +#endif // CFSTORE_EXAMPLE3_APP diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example4/example4.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example4/example4.cpp new file mode 100644 index 0000000000..242e3b3ce4 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example4/example4.cpp @@ -0,0 +1,208 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** @file example4.cpp Test case to demonstrate a subset of the API functions each work correctly. + * + * Test case created from Issue 10 code supplied by Motti Gondabi. The code: + * - creates a KV + * - writes the KV + * - closes the KV + * - flushes the KV + * - opens the KV. + * - deletes the KV. + * - flushes empty configuration store. + * + * The test case makes sure that the implementation can flush an empty configuration store + * without causing errors. This has only been possible since flash-journal-strategy-sequential + * v0.4.0. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + +#include +#include +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_example4_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* defines */ +/// @cond CFSTORE_DOXYGEN_DISABLE +#define PvMemSet memset +#define PvStrLen strlen +#define PvKeyValue_t cfstore_kv_data_t + +ARM_CFSTORE_DRIVER *gCfStoreDriver = &cfstore_driver; +/// @endcond + +static control_t cfstore_example4_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_CAPABILITIES caps;; + + (void) call_count; + + /* initialise the context */ + caps = gCfStoreDriver->GetCapabilities(); + CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, caps.asynchronous_ops); + if(caps.asynchronous_ops == true){ + /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true + * This means the test will conveniently pass when run in CI as part of async mode testing */ + CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); + return CaseNext; + } + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + return CaseNext; +} + + +/* used for sync mode build only */ +#if defined STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS && STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS==0 + +static const PvKeyValue_t testDataKeyValue[] = { + { "com.arm.mbed.spv.assets.dtls", "This Is my DTLS Secret" }, + { "com.arm.mbed.spv.assets.asset1.payload", "The Rolling Stone" }, + { "com.arm.mbed.spv.assets.asset2.payload", "Grumpy old man" }, + { "com.arm.mbed.spv.assets.asset3.payload", "Delete this asset payload" }, +}; + + +static int32_t CreateKeyValueStore( + const char *keyName, + const char *data, + ARM_CFSTORE_SIZE *dataLength, + ARM_CFSTORE_KEYDESC *keyDesc) +{ + int32_t cfsStatus = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE valueLength = 0; + ARM_CFSTORE_HANDLE_INIT(hkey); + + valueLength = *dataLength; + cfsStatus = gCfStoreDriver->Create(keyName, valueLength, keyDesc, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Create() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + valueLength = *dataLength; + cfsStatus = gCfStoreDriver->Write(hkey, data, &valueLength); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Write() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: valueLength != *dataLength\n", __func__); + TEST_ASSERT_MESSAGE(valueLength == *dataLength, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Flush() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + return ARM_DRIVER_OK; +} + + +static control_t cfstore_example4_test_01(const size_t call_count) +{ + int32_t cfsStatus = ARM_DRIVER_ERROR; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_SIZE valueLen; + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_HANDLE_INIT(hkey); + + (void) call_count; + PvMemSet(&kdesc, 0, sizeof(kdesc)); + + kdesc.drl = ARM_RETENTION_NVM; + valueLen = PvStrLen(testDataKeyValue[0].value); + + cfsStatus = gCfStoreDriver->Initialize(NULL, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Initialize() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = CreateKeyValueStore(testDataKeyValue[0].key_name, testDataKeyValue[0].value, &valueLen, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: CreateKeyValueStore() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + PvMemSet(&flags, 0, sizeof(flags)); + + cfsStatus = gCfStoreDriver->Open(testDataKeyValue[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Open() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Delete(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Delete() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Flush() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() failed (cfsStatus=%d)\n", __func__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + return CaseNext; +} +#endif // STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(400, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE4_test_00", cfstore_example4_test_00), +#if defined STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS && STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS==0 + Case("EXAMPLE4_test_01", cfstore_example4_test_01), +#endif // STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example5/example5.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example5/example5.cpp new file mode 100644 index 0000000000..926c1eceb3 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example5/example5.cpp @@ -0,0 +1,330 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Test cases to add and delete KVs in the CFSTORE. + */ + + +/** @file example5.cpp + * + * Test case to demonstrate each API function works correctly. + * + * \par Example 5 Notes + * + * This flash-journal synchronous mode example test does the following CFSTORE operations: + * - initialises + * - creates a key-value pair (KV). + * - writes the data for the KV + * - closes KV. + * - flushes the KV to flash + * - flushes the KV to flash again + * - uninitialises + * - initialises + * - opens KV for reading. + * - deletes KV + * - closes KV. + * - flushes the updated state to flash to store the removal of the deleted KV. + * - uninitialises + * - stops + * + * This test is coded so as to work only in flash journal sync mode + * i.e. with caps.asynchronous_ops == false + * + * The test leaves the flash in the same state as at the beginning of the test so + * it can be run a second time on the device without flashing, and the test should + * still work. + * + * \par How to Build Example5 as a Stand-alone Application + * + * This example can be build as a stand-alone application as follows: + * - Create a new mbed application using the `mbed new .` command. + * - Copy this file example5.cpp from the to the top level application directory and rename the file to main.cpp. + * - Build the application with `mbed compile -v -m -t -DCFSTORE_EXAMPLE5_APP` e.g. `mbed compile -v -m K64F -t GCC_ARM -DCFSTORE_EXAMPLE5_APP`. + */ + +#include "mbed.h" +#include "Driver_Common.h" + +#ifndef CFSTORE_EXAMPLE5_APP +/* when built as Configuration-Store example, include greentea support otherwise omit */ +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#else // CFSTORE_EXAMPLE5_APP +/* map utest types for building as stand alone example */ +#define control_t void +#define CaseNext +#endif // CFSTORE_EXAMPLE5_APP + +#include "cfstore_config.h" +#include "configuration_store.h" + +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED +#include "flash_journal_strategy_sequential.h" +#include "flash_journal.h" +#include "Driver_Common.h" +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + +#include +#include +#include + + +#ifndef CFSTORE_EXAMPLE5_APP +using namespace utest::v1; +#endif + + +/// @cond CFSTORE_DOXYGEN_DISABLE +#define CFSTORE_EX5_TEST_ASSERT(Expr) if (!(Expr)) { printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX5_TEST_ASSERT_EQUAL(expected, actual) if ((expected) != (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX5_TEST_ASSERT_NOT_EQUAL(expected, actual) if ((expected) == (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} + +#define CFSTORE_EX5_TEST_ASSERT_MSG(Expr, _fmt, ...) \ + do \ + { \ + if (!(Expr)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + while (1) ; \ + } \ + }while(0); + +#define CFSTORE_EX5_LOG(_fmt, ...) \ + do \ + { \ + printf(_fmt, __VA_ARGS__); \ + }while(0); + + +const char* cfstore_ex5_opcode_str[] = +{ + "UNDEFINED", + "CFSTORE_OPCODE_CLOSE", + "CFSTORE_OPCODE_CREATE", + "CFSTORE_OPCODE_DELETE", + "CFSTORE_OPCODE_FIND", + "CFSTORE_OPCODE_FLUSH", + "CFSTORE_OPCODE_GET_KEY_NAME", + "CFSTORE_OPCODE_GET_STATUS", + "CFSTORE_OPCODE_GET_VALUE_LEN", + "CFSTORE_OPCODE_INITIALIZE", + "CFSTORE_OPCODE_OPEN", + "CFSTORE_OPCODE_POWER_CONTROL", + "CFSTORE_OPCODE_READ", + "CFSTORE_OPCODE_RSEEK", + "CFSTORE_OPCODE_UNINITIALIZE", + "CFSTORE_OPCODE_WRITE", + "CFSTORE_OPCODE_MAX" +}; + +const char* cfstore_ex5_kv_name = "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.well.well"; +const char* cfstore_ex5_kv_value = "TheRollingStone"; +#define CFSTORE_EX5_RSEEK_OFFSET 10 /* offset to S of Stone */ + +/// @cond CFSTORE_DOXYGEN_DISABLE +typedef struct cfstore_EXAMPLE5_ctx_t +{ + ARM_CFSTORE_CAPABILITIES caps; + uint8_t hkey[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_next_buf[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_prev_buf[CFSTORE_HANDLE_BUFSIZE]; + ARM_CFSTORE_HANDLE hkey_next; + ARM_CFSTORE_HANDLE hkey_prev; + ARM_CFSTORE_SIZE len; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; +} cfstore_EXAMPLE5_ctx_t; + +static cfstore_EXAMPLE5_ctx_t cfstore_EXAMPLE5_ctx_g; + +extern ARM_CFSTORE_DRIVER cfstore_driver; +ARM_CFSTORE_DRIVER *cfstore_drv = &cfstore_driver; +/// @endcond + + +/* @brief test startup code to reset flash + */ +int32_t cfstore_test_startup(void) +{ + ARM_CFSTORE_CAPABILITIES caps = cfstore_driver.GetCapabilities(); + CFSTORE_EX5_LOG("INITIALIZING: caps.asynchronous_ops=%d\n", (int) caps.asynchronous_ops); + +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED + int32_t ret = ARM_DRIVER_ERROR; + static FlashJournal_t jrnl; + extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); + const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); + + ret = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (ret=%d)\r\n", __func__, (int) ret); + + ret = FlashJournal_reset(&jrnl); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_reset() failed (ret=%d)\r\n", __func__, (int) ret); +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + + return ARM_DRIVER_OK; +} + + +static void cfstore_ex5_test_01(cfstore_EXAMPLE5_ctx_t* ctx) +{ + int32_t ret; + + CFSTORE_EX5_LOG("INITIALIZING1%s", "\r\n"); + ret = cfstore_drv->Initialize(NULL, NULL); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("CREATING%s", "\r\n"); + memset(&ctx->kdesc, 0, sizeof(ARM_CFSTORE_KEYDESC)); + ctx->kdesc.drl = ARM_RETENTION_NVM; + ctx->len = strlen(cfstore_ex5_kv_value); + ret = cfstore_drv->Create(cfstore_ex5_kv_name, ctx->len, &ctx->kdesc, ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Create() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("WRITING%s", "\r\n"); + ctx->len = strlen(cfstore_ex5_kv_value); + ret = cfstore_drv->Write(ctx->hkey, cfstore_ex5_kv_value, &ctx->len); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Write() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_TEST_ASSERT_MSG(ret == (int32_t) strlen(cfstore_ex5_kv_value), "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ret, (int32_t) strlen(cfstore_ex5_kv_value)); + CFSTORE_EX5_TEST_ASSERT_MSG(ret == (int32_t) ctx->len, "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ret, (int32_t) ctx->len); + + CFSTORE_EX5_LOG("CLOSING1%s", "\r\n"); + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("FLUSHING1%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED + /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED => flash storage support present. + * if this was not the case (i.e. cfstore running SRAM in memory mode) then + * we dont compile in the Uninitialize()/Initialize() as the + * Uninitialize() clears the sram */ + CFSTORE_EX5_LOG("UNINITIALIZING1%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Uninitialize() should return ret >= 0 for synch mode(ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("INITIALIZING2%s", "\r\n"); + ret = cfstore_drv->Initialize(NULL, NULL); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); +#endif + + CFSTORE_EX5_LOG("OPENING%s", "\r\n"); + memset(&ctx->flags, 0, sizeof(ctx->flags)); + memset(&ctx->hkey, 0, CFSTORE_HANDLE_BUFSIZE); + ret = cfstore_drv->Open(cfstore_ex5_kv_name, ctx->flags, ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Open() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("DELETE1%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH + 1); + ret = cfstore_drv->Delete(ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Delete() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("CLOSING2%s", "\r\n"); + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("FLUSHING3%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("OPEN2 %s", "\r\n"); + memset(&ctx->flags, 0, sizeof(ctx->flags)); + memset(&ctx->hkey, 0, CFSTORE_HANDLE_BUFSIZE); + ret = cfstore_drv->Open(cfstore_ex5_kv_name, ctx->flags, ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, "%s:Error: Find() failed to return expected value of ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("FLUSHING2%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error:2: Flush() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("UNINITIALIZING3%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Uninitialize() should return ret >= 0 for synch mode(ret=%ld)\r\n", __func__, ret); + CFSTORE_EX5_LOG("***************%s", "\r\n"); + CFSTORE_EX5_LOG("*** SUCCESS ***%s", "\r\n"); + CFSTORE_EX5_LOG("***************%s", "\r\n"); + return; +} + +static control_t cfstore_EXAMPLE5_app_start(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_EXAMPLE5_ctx_t* ctx = &cfstore_EXAMPLE5_ctx_g; + + (void) call_count; + + /* initialise the context */ + memset(ctx, 0, sizeof(cfstore_EXAMPLE5_ctx_t)); + ctx->hkey_next = ctx->hkey_next_buf; + ctx->hkey_prev = ctx->hkey_prev_buf; + ctx->caps = cfstore_drv->GetCapabilities(); + CFSTORE_EX5_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops); + if(ctx->caps.asynchronous_ops == true){ + /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true + * This means the test will conveniently pass when run in CI as part of async mode testing */ + CFSTORE_EX5_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); + return CaseNext; + } + ret = cfstore_test_startup(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + cfstore_ex5_test_01(ctx); + return CaseNext; +} + +#ifndef CFSTORE_EXAMPLE5_APP +/* when built as Configuration-Store example, include greentea support otherwise omit */ + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE5_test_01_start", cfstore_EXAMPLE5_app_start), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + + +#else // CFSTORE_EXAMPLE5_APP + +// stand alone Configuration-Store-Example +void app_start(int argc __unused, char** argv __unused) +{ + cfstore_EXAMPLE5_app_start(0); +} + + +#endif // CFSTORE_EXAMPLE5_APP diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/find/find.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/find/find.cpp new file mode 100644 index 0000000000..3d19148b05 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/find/find.cpp @@ -0,0 +1,472 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + */ + +/** @file find.cpp Test cases to find KVs in the CFSTORE using the drv->Find() interface. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_find_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.find.box1"); +UVISOR_BOX_CONFIG(cfstore_find_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_find_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + return CaseNext; +} + +/** @brief test to call cfstore_find() with a key_name string that exceeds + * the maximum length + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_find_test_01(const size_t call_count) +{ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_DBGLOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_find_utest_msg_g); + return CaseNext; +} + + +/** @brief test to call cfstore_find() with key_name that in includes + * illegal characters + * - the character can be at the beginning of the key_name + * - the character can be at the end of the key_name + * - the character can be somewhere within the key_name string + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_find_test_02(const size_t call_count) +{ + /*todo: implement test + * + * specify the allowable characters in a set. + * e.g. "0123456789ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvwxyz.[]*" + * and use this set as the sell of allowable character codes. + * All other 0-255 values for character codes should not be found in the key_name string + * but the function should be tested for what happens in that case. + * + * Some code may be common here with other functions requiring a key_name e.g. cfstore_find( + */ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_DBGLOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_find_utest_msg_g); + return CaseNext; +} + + +/** @brief test to call cfstore_find() with key_name that in includes + * illegal characters + * - the character can be at the beginning of the key_name + * - the character can be at the end of the key_name + * - the character can be somewhere within the key_name string + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_find_test_03_end(const size_t call_count) +{ + char* read_buf = NULL; + const uint8_t key_name_max_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + char key_name_buf[key_name_max_len]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_SIZE max_len = 0; + cfstore_kv_data_t* node; + cfstore_kv_data_t* client_node = cfstore_test_init_1_data; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_HANDLE_INIT(next); + + CFSTORE_DBGLOG("%s:entered\r\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + memset(key_name_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + + /*scan for max length of value blob*/ + node = client_node; + while(node->key_name != NULL) + { + len = strlen(node->value); + if(len > max_len){ + max_len = len; + } + node++; + } + max_len++; /* space for a terminating null, if required */ + read_buf = (char*) malloc(max_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to allocated read buffer \r\n", __func__); + TEST_ASSERT_MESSAGE(read_buf != NULL, cfstore_find_utest_msg_g); + + ret = cfstore_test_init_1(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialise cfstore area with entries\r\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + /* now find and read back the key values */ + ret = ARM_DRIVER_ERROR; + node = client_node; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:About to find node (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + ret = drv->Find(node->key_name, prev, next); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to find node (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Find failed to return valid key handle\r\n", __func__); + TEST_ASSERT_MESSAGE(next != NULL, cfstore_find_utest_msg_g); + + ret = drv->GetValueLen(next, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + if(len > 0) { + ret = drv->Read(next, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read value (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + /* check read data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\r\n", __func__, read_buf, node->key_name, node->value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, cfstore_find_utest_msg_g); + + } + read_buf[len] = '\0'; + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("Successfully found KV and read value data (key_name=\"%s\", value=\"%s\")\r\n", node->key_name, read_buf); + memset(read_buf, 0, len); + drv->Close(next); + node++; + } + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + return CaseNext; +} + + +/** @brief TODO: write test that uses cfstore_find_test_04_kv_data to grow {key, value} + * from 1 char to 221 chars long. + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +/* + * use this data: + * static cfstore_kv_data_t cfstore_find_test_04_kv_data[] = { + * { "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "abcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyz"}, + * { NULL, NULL}, + * }; + * + */ +control_t cfstore_find_test_04(const size_t call_count) +{ + /*todo: implement test + * + * */ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_DBGLOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_find_utest_msg_g); + return CaseNext; +} + + +/** + * @brief function to test whether a key name is valid, can be added to the + * cfstore and found by a wildcard string + * + * @param key_name + * name of the key to create in the store + * @param match + * string to use to try and find key_name in the cfstore + * @param should_find + * if true, then 'match' should find 'key_name' in the store + * if false, then 'match' should not find 'key_name' in the store + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static bool cfstore_find_key_name_validate(const char *key_name, const char *match, bool should_find) +{ + bool bret = true; + bool btest_status = false; + char* test_data = (char*) "test_data"; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + + /* create */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(test_data); + ret = cfstore_test_create((const char*) key_name, test_data, &len, &kdesc); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to create kv (key_name=%s.\r\n", "cfstore_find_test_05_ex", key_name); + return ret; + } + ret = cfstore_test_kv_is_found(match, &bret); + if(ret < ARM_DRIVER_OK && ret != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_ERRLOG("%s:Error: cfstore_test_kv_is_found() failed.\r\n", "cfstore_find_test_05_ex"); + return ret; + } + /* dont not use any functions that require finding the created item as they may not work, + * depending on the construction of the test key_name & match strings */ + if(should_find == bret) + { + CFSTORE_DBGLOG("%s:Success: Find() behaved as expected.\r\n", "cfstore_find_test_05_ex"); + btest_status = true; + } + else + { + CFSTORE_ERRLOG("cfstore_find_test_05_ex: Failed match on %s vs. %s\n", key_name, match); + } + /*delete using the actual name */ + ret = cfstore_test_delete((const char*) key_name); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to delete kv (key_name=%s)(ret=%d).\r\n", "cfstore_find_test_05_ex", key_name, (int)ret); + } + return btest_status; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +typedef struct cfstore_find_key_name_validate_t { + const char* key_name; + const char* match; + uint32_t f_allowed : 1; +} cfstore_find_key_name_validate_t; + +cfstore_find_key_name_validate_t cfstore_find_test_05_data[] = { + { "yotta.hello-world.animal{wobbly-dog}{foot}backLeft", "yotta.hello-world.animal{*}{foot}backLeft", true}, + { "yotta.hello-world.animal{wobbly-dog}{foot}backLeft", "yotta.hello-world.animal{wobbly-dog}{*}backLeft", true}, + { "yotta.hello-world.animal{wobbly-dog}{foot}backLeft", "yotta.hello-world.animal{wobbly-dog}{*}*", true}, + { "yotta.hello-world.animal{1}{foot}backLeft", "yotta.hello-world.animal{?}{foot}backLeft", false}, + { "xyz", "xyz", true}, + { "xyzijkXYZ", "XYZ", false}, + { "xyzijkXYZ", "*XYZ", true}, + { "xyzijkXYZ", "xyz*XYZ", true}, + { "xyzijkXYZ", "*yz*XYZ", true}, + { "xyzijkXYZ", "*ijk*", true}, + { NULL, NULL, false}, +}; +/// @endcond + + +/** @brief test whether a key name in the above table are valid, can be added to the + * cfstore and found by a wildcard string + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_find_test_05_end(const size_t call_count) +{ + bool ret = false; + int32_t ret32 = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_find_key_name_validate_t* node = cfstore_find_test_05_data; + + (void) call_count; + + while(node->key_name != NULL) + { + ret = cfstore_find_key_name_validate(node->key_name, node->match, node->f_allowed); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: test failed (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == true, cfstore_find_utest_msg_g); + node++; + } + + ret32 = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret32 >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +#define CFSTORE_FIND_TEST_06_ENTRY_MATCH_03 { "0123456789abcdef0123456.yxxx.3", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_MATCH_05 { "0123456789abcdef0123456.yxxx.5", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_MATCH_07 { "0123456789abcdef0123456.yxxx.7", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_MATCH_09 { "0123456789abcdef0123456.yxxx.9", "abcdefghijklmnopqrstuvwxyz"} + +#define CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_01 { "0123456789abcdef0123456.xxxx.1", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_02 { "0123456789abcdef0123456.xxxx.2", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_04 { "0123456789abcdef0123456.xxxx.4", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_06 { "0123456789abcdef0123456.xxxx.6", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_08 { "0123456789abcdef0123456.xxxx.8", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_10 { "0123456789abcdef0123456.xxxx.10", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_11 { "0123456789abcdef0123456.xxxx.11", "abcdefghijklmnopqrstuvwxyz"} + +/* table 1: to initialise cfstore with CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01 */ +static cfstore_kv_data_t cfstore_find_test_06_data[] = { + CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_01, + CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_02, + CFSTORE_FIND_TEST_06_ENTRY_MATCH_03, + CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_04, + CFSTORE_FIND_TEST_06_ENTRY_MATCH_05, + CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_06, + CFSTORE_FIND_TEST_06_ENTRY_MATCH_07, + CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_08, + CFSTORE_FIND_TEST_06_ENTRY_MATCH_09, + CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_10, + CFSTORE_FIND_TEST_06_ENTRY_NOMATCH_11, + { NULL, NULL}, +}; + +static cfstore_kv_data_t cfstore_find_test_06_data_match_results[] = { + CFSTORE_FIND_TEST_06_ENTRY_MATCH_03, + CFSTORE_FIND_TEST_06_ENTRY_MATCH_05, + CFSTORE_FIND_TEST_06_ENTRY_MATCH_07, + CFSTORE_FIND_TEST_06_ENTRY_MATCH_09, + { NULL, NULL}, +}; +/// @endcond + + +/** + * @brief test to use find to find at least 2 entries in the cfstore, + * but the query string doesnt match the last 2 entries in the + * store. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_find_test_06_end(const size_t call_count) +{ + const char* key_name_query = "0123456789abcdef0123456.y*"; + char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint8_t len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + int32_t ret = ARM_DRIVER_ERROR; + int32_t find_count = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(next); + ARM_CFSTORE_HANDLE_INIT(prev); + cfstore_kv_data_t* node = NULL; + + ret = cfstore_test_create_table(cfstore_find_test_06_data); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to add cfstore_find_test_06_data table data (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + while((ret = drv->Find(key_name_query, prev, next)) == ARM_DRIVER_OK) + { + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = drv->GetKeyName(next, key_name, &len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("Error: failed to get key name%s", "\n"); + break; + } + CFSTORE_LOG("%s:Found entry key_name=%s\n", __func__, key_name); + node = cfstore_find_test_06_data_match_results; + while(node->key_name != NULL){ + if(strncmp(node->key_name, key_name, CFSTORE_KEY_NAME_MAX_LENGTH) == 0){ + find_count++; + break; + } + node++; + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to find match in match table for %s.\n", __func__, key_name); + TEST_ASSERT_MESSAGE(node->key_name != NULL, cfstore_find_utest_msg_g); + + CFSTORE_HANDLE_SWAP(prev, next); + } + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: find_count=%d doesnt match the number of entries in match table = %d.\n", __func__, (int) find_count, (int) (sizeof(cfstore_find_test_06_data_match_results)/sizeof(cfstore_kv_data_t))-1); + TEST_ASSERT_MESSAGE(find_count == (sizeof(cfstore_find_test_06_data_match_results)/sizeof(cfstore_kv_data_t))-1, cfstore_find_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: expected ret == ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, but ret = %d.\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_find_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(400, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FIND_test_00", cfstore_find_test_00), + Case("FIND_test_01", cfstore_find_test_01), + Case("FIND_test_02", cfstore_find_test_02), + Case("FIND_test_03_start", cfstore_utest_default_start), + Case("FIND_test_03_end", cfstore_find_test_03_end), + Case("FIND_test_04", cfstore_find_test_04), + Case("FIND_test_05_start", cfstore_utest_default_start), + Case("FIND_test_05_end", cfstore_find_test_05_end), + Case("FIND_test_06_start", cfstore_utest_default_start), + Case("FIND_test_06_end", cfstore_find_test_06_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/find2/find2.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/find2/find2.cpp new file mode 100644 index 0000000000..640470fbbb --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/find2/find2.cpp @@ -0,0 +1,245 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** @file find2.cpp Test cases to find KVs in the CFSTORE using the drv->Find() interface. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_find2_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.find2.box1"); +UVISOR_BOX_CONFIG(cfstore_find2_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +/// @cond CFSTORE_DOXYGEN_DISABLE +#ifdef CFSTORE_DEBUG +#define CFSTORE_FIND2_GREENTEA_TIMEOUT_S 360 +#else +#define CFSTORE_FIND2_GREENTEA_TIMEOUT_S 60 +#endif +#define CFSTORE_FIND2_TEST_02_VALUE_SIZE 191 + +extern ARM_CFSTORE_DRIVER cfstore_driver; + +void cfstore_find2_callback(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle) +{ + (void) status; + (void) cmd_code; + (void) client_context; + (void) handle; + return; +} +/// @endcond + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_find2_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find2_utest_msg_g); + return CaseNext; +} + + +static control_t cfstore_find2_test_01(const size_t call_count) +{ + char keyBuffer[128] = "com.arm.mbed.manifest-manager.root.AQAAAAAAAAA-.manifest"; + int32_t rc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_HANDLE_INIT(prev); + + // Initialize the config store + (void) call_count; + cfstore_driver.Initialize(cfstore_find2_callback, NULL); + cfstore_driver.PowerControl(ARM_POWER_FULL); + + // Find the target key + rc = cfstore_driver.Find(keyBuffer, prev, hkey); + + // If the target key was not found + if (rc == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + ARM_CFSTORE_KEYDESC kdesc = { + .acl = { + .perm_owner_read = 1, + .perm_owner_write = 1, + .perm_owner_execute = 0, + .perm_other_read = 1, + .perm_other_write = 0, + .perm_other_execute = 0, + /* added initialisers */ + .reserved = 0 + }, .drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE, + .security = { + .acls = 1, + .rollback_protection = 0, + .tamper_proof = 0, + .internal_flash = 0, + /* added initialisers */ + .reserved1 = 0, + .software_attacks = 0, + .board_level_attacks = 0, + .chip_level_attacks = 0, + .side_channel_attacks = 0, + .reserved2 = 0 + }, + .flags = { + .continuous = 0, + .lazy_flush = 1, + .flush_on_close = 1, + .read = 0, + .write = 1, + .execute = 0, + .storage_detect = 1, + /* added initialisers */ + .reserved = 0 + } + }; + + // Create the target key + rc = cfstore_driver.Create(keyBuffer, 191, &kdesc, hkey); + } + return CaseNext; +} + + +/* fixed version of brendans code */ +static control_t cfstore_find2_test_02(const size_t call_count) +{ + char keyBuffer[128] = "com.arm.mbed.manifest-manager.root.AQAAAAAAAAA-.manifest"; + + int32_t rc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_SIZE length; + char value[CFSTORE_FIND2_TEST_02_VALUE_SIZE]; + + // Initialize the config store + (void) call_count; + cfstore_driver.Initialize(NULL, NULL); /* non-null client_callback not supported for MBED_V_0_1_x */ + cfstore_driver.PowerControl(ARM_POWER_FULL); + memset(value, 0, 191); + + // Find the target key + rc = cfstore_driver.Find(keyBuffer, prev, hkey); + + // If the target key was not found + if (rc == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + ARM_CFSTORE_KEYDESC kdesc = { + .acl = { + .perm_owner_read = 1, + .perm_owner_write = 1, + .perm_owner_execute = 0, + .perm_other_read = 1, + .perm_other_write = 0, + .perm_other_execute = 0, + .reserved = 0 + }, .drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE, /* DATA_RETENTION_NVM not supported for MBED_V_0_1_x */ + .security = { + .acls = 0, /* protection against internal software attacks using ACLs not supported for MBED_V_0_1_x */ + .rollback_protection = 0, + .tamper_proof = 0, + .internal_flash = 0, + .reserved1 = 0, + .software_attacks = 0, + .board_level_attacks = 0, + .chip_level_attacks = 0, + .side_channel_attacks = 0, + .reserved2 = 0 + }, + .flags = { + .continuous = 0, + .lazy_flush = 0, /* lazy flush not supported for MBED_V_0_1_x */ + .flush_on_close = 0, /* flush on close not supported for MBED_V_0_1_x */ + .read = 0, + .write = 1, + .execute = 0, + .storage_detect = 0, /* storage detect supported for MBED_V_0_1_x */ + /* added initialisers */ + .reserved = 0 + + } + }; + + // Create the target key + rc = cfstore_driver.Create(keyBuffer, 191, &kdesc, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: failed to create key\n", __func__); + TEST_ASSERT_MESSAGE(rc >= ARM_DRIVER_OK, cfstore_find2_utest_msg_g); + + strncpy(value, "MyValueData", CFSTORE_FIND2_TEST_02_VALUE_SIZE); + length = strlen(value); + rc = cfstore_driver.Write(hkey, value, &length); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: failed to write key\n", __func__); + TEST_ASSERT_MESSAGE(rc >= ARM_DRIVER_OK, cfstore_find2_utest_msg_g); + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("Success!%s", "\n"); + } + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(CFSTORE_FIND2_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FIND2_test_00", cfstore_find2_test_00), + Case("FIND2_test_01", cfstore_find2_test_01), + Case("FIND2_test_02", cfstore_find2_test_02), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash/flash.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash/flash.cpp new file mode 100644 index 0000000000..5b785b71ef --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash/flash.cpp @@ -0,0 +1,695 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file flash.cpp Test cases to flush KVs in the CFSTORE using the Flash-Journal interface. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED +#include "flash_journal_strategy_sequential.h" +#include "flash_journal.h" +#include "Driver_Common.h" +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.flash.box1"); +UVISOR_BOX_CONFIG(cfstore_flash_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + + +/* shared code common to both sync and async test cases */ + +/* + * Defines + * + */ +/// @cond CFSTORE_DOXYGEN_DISABLE +#define CFSTORE_FREE free +#define CFSTORE_MALLOC malloc +#define CFSTORE_REALLOC realloc +#define CFSTORE_FLASH_STACK_BUF_SIZE 64 +#define CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE 8 +#define CFSTORE_TEST_DATA_KEYNAME "com.arm.mbed.configurationstore.test.flush.cfstoreflushtest02" +#define CFSTORE_TEST_DATA_KEYNAME_SHORT "com.arm" +#define CFSTORE_TEST_DATA_VALUE_INIT "\1" +#define CFSTORE_TEST_DATA_KEYNAME_SIZE (sizeof(CFSTORE_TEST_DATA_KEYNAME) - 1) +#define CFSTORE_TEST_DATA_VALUE_SIZE (sizeof(CFSTORE_TEST_DATA_VALUE_INIT) - 1) +#define CFSTORE_FLASH_UTEST_MSG_BUF_SIZE 256 +#define CFSTORE_FLASH_MTD_ASYNC_OPS_ON 1 +#define CFSTORE_FLASH_MTD_ASYNC_OPS_OFF 0 +#define CFSTORE_FLASH_CASE_TIMEOUT_MS 5000 +/// @endcond + +/* + * Globals + */ + +/// @cond CFSTORE_DOXYGEN_DISABLE +char cfstore_flash_utest_msg_g[CFSTORE_FLASH_UTEST_MSG_BUF_SIZE]; +/// @endcond + +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED +uint16_t cfstore_flash_mtd_async_ops_g = 0; +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_flush_test_01_kv_data[] = { + { CFSTORE_TEST_DATA_KEYNAME, CFSTORE_TEST_DATA_VALUE_INIT}, + { NULL, NULL}, +}; + + +/* @brief key value header structure defining key_name length, value length + * @note + * 8 bytes long */ +typedef struct cfstore_area_header_t +{ + uint32_t vlength; + uint8_t klength; + uint8_t perm_owner_read : 1; + uint8_t perm_owner_write : 1; + uint8_t perm_owner_execute : 1; + uint8_t perm_other_read : 1; + uint8_t perm_other_write : 1; + uint8_t perm_other_execute : 1; + uint8_t reserved : 2; + uint8_t refcount; + struct flags_t { + uint8_t deleting : 1; + uint8_t reserved : 7; + } flags ; +} cfstore_area_header_t; + + +/* @brief data structure for managing test data */ +typedef struct cfstore_flash_data_blob_t { + cfstore_area_header_t hdr; + uint8_t data[CFSTORE_TEST_DATA_KEYNAME_SIZE + CFSTORE_TEST_DATA_VALUE_SIZE]; /* 61 bytes for key_name, 1 byte for value */ +} cfstore_flash_data_blob_t; + +/* + * Defines + * + * CFSTORE_FLASH_AREA_SIZE_MIN + * valid sizes of areas should always be greater than the size of the header, and therefore + * greater than this value, which is defined as smaller than the header size + */ +#define CFSTORE_FLASH_AREA_SIZE_MIN (sizeof(cfstore_area_header_t) - 1) + +/* + * Shared implementation between sync and async tests + */ + +/* print key name string from area where key_name is not null terminated*/ +static void cfstore_dump_key_name(uint8_t* keyname, uint8_t len, const char* tag) +{ + char blob_data[CFSTORE_KEY_NAME_MAX_LENGTH]; + + (void) tag; + assert(keyname != NULL); + assert(tag != NULL); + assert(len > 0); + memcpy(blob_data, keyname, len); + blob_data[len] = '\0'; + CFSTORE_DBGLOG("%s:keyname=%s\r\n", tag, blob_data); +} + +/* @brief test fsm states and events */ +typedef enum cfstore_flash_fsm_state_t { + cfstore_flash_fsm_state_initializing = 0, + cfstore_flash_fsm_state_reading, + cfstore_flash_fsm_state_writing, + cfstore_flash_fsm_state_committing, + cfstore_flash_fsm_state_max +} cfstore_flash_fsm_state_t; + +/* @brief test fsm events */ +typedef enum cfstore_flash_fsm_event_t { + cfstore_flash_fsm_event_init_done = 0, + cfstore_flash_fsm_event_read_done, + cfstore_flash_fsm_event_write_done, + cfstore_flash_fsm_event_commit_done, + cfstore_flash_fsm_event_max, +} cfstore_flash_fsm_event_t; + +typedef void (*cfstore_flash_fsm_handler)(void* ctx); + +typedef struct cfstore_fsm_t +{ + cfstore_flash_fsm_state_t state; + cfstore_flash_fsm_event_t event; +} cfstore_fsm_t; + +typedef struct cfstore_flash_ctx_t +{ + uint8_t* area_0_head; + uint8_t* area_0_tail; + FlashJournal_t jrnl; + uint64_t expected_blob_size; + cfstore_fsm_t fsm; + int32_t status; + FlashJournal_OpCode_t cmd_code; + uint64_t expected_read_size; +} cfstore_flash_ctx_t; + +/* + * Globals + */ +static cfstore_flash_ctx_t cfstore_flash_ctx_g; +static const char* cfstore_flash_opcode_str[] = +{ + "FLASH_JOURNAL_OPCODE_INITIALIZE", + "FLASH_JOURNAL_OPCODE_GET_INFO", + "FLASH_JOURNAL_OPCODE_READ_BLOB", + "FLASH_JOURNAL_OPCODE_LOG_BLOB", + "FLASH_JOURNAL_OPCODE_COMMIT", + "FLASH_JOURNAL_OPCODE_RESET", +}; + +#ifdef CFSTORE_DEBUG +static const char* cfstore_flash_state_str[] = +{ + "initializing", + "reading", + "writing", + "committing", + "unknown" +}; + +static const char* cfstore_flash_event_str[] = +{ + "init_done", + "read_done", + "write_done", + "commit_done", + "unknown" +}; +#endif /* CFSTORE_DEBUG */ + +/* + * Forward decl + */ +static void cfstore_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_flash_fsm_event_t event, void* context); +static void cfstore_fsm_state_set(cfstore_fsm_t* fsm, cfstore_flash_fsm_state_t new_state, void* ctx); + +/* + * context related methods + */ + +/* @brief get a pointer to the global context data structure */ +static cfstore_flash_ctx_t* cfstore_flash_ctx_get(void) +{ + return &cfstore_flash_ctx_g; +} + + +/* @brief flash journal asynchronous callback handler */ +void cfstore_flash_test_01_callback(int32_t status, FlashJournal_OpCode_t cmd_code) +{ + cfstore_flash_ctx_t* ctx = cfstore_flash_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: status=%d, cmd_code=%d (%s)\r\n", __func__, (int) status, (int) cmd_code, cfstore_flash_opcode_str[cmd_code]); + switch(cmd_code) + { + case FLASH_JOURNAL_OPCODE_INITIALIZE: + ctx->fsm.event = cfstore_flash_fsm_event_init_done; + break; + case FLASH_JOURNAL_OPCODE_READ_BLOB: + ctx->fsm.event = cfstore_flash_fsm_event_read_done; + break; + case FLASH_JOURNAL_OPCODE_LOG_BLOB: + ctx->fsm.event = cfstore_flash_fsm_event_write_done; + break; + case FLASH_JOURNAL_OPCODE_COMMIT: + ctx->fsm.event = cfstore_flash_fsm_event_commit_done; + break; + case FLASH_JOURNAL_OPCODE_GET_INFO: + case FLASH_JOURNAL_OPCODE_RESET: + default: + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: received asynchronous notification for opcode=%d (%s) when api call should have been synchronous", __func__, cmd_code, cmd_code <= FLASH_JOURNAL_OPCODE_RESET ? cfstore_flash_opcode_str[cmd_code] : "unknown"); + TEST_ASSERT_MESSAGE(false, cfstore_flash_utest_msg_g) + return; + } + ctx->status = status; + ctx->cmd_code = cmd_code; + cfstore_fsm_state_handle_event(&ctx->fsm, ctx->fsm.event, (void*) ctx); + return; +} + + +/* @brief fsm handler called on entry to initializing state */ +static void cfstore_flash_fsm_init_on_entry(void* context) +{ + /* round up cfstore_flash_data_blob_t to nearest k64f program unit size */ + const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); + FlashJournal_Info_t info; + FlashJournal_Status_t status = JOURNAL_STATUS_ERROR; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + memset(&info, 0, sizeof(info)); + + /* FlashJournal_initialize() is potentially asynchronous */ + status = (FlashJournal_Status_t) FlashJournal_initialize(&ctx->jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, cfstore_flash_test_01_callback); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize flash journaling layer (status=%d)\r\n", __func__, status); + TEST_ASSERT_MESSAGE(status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + /* if status > 0, expect async callback, otherwise initialisation has been completed */ + if(status > 0) { + cfstore_flash_test_01_callback(status, FLASH_JOURNAL_OPCODE_INITIALIZE); + } + return; +} + +/* brief callback handler when in state initializing */ +static void cfstore_flash_fsm_initializing(void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in initializing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flash_fsm_state_initializing, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, FLASH_JOURNAL_OPCODE_INITIALIZE); + TEST_ASSERT_MESSAGE(ctx->cmd_code == FLASH_JOURNAL_OPCODE_INITIALIZE, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%d\r\n", __func__, (int) ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + /* only change state if status > 0*/ + if(ctx->status > 0){ + cfstore_fsm_state_set(&ctx->fsm, cfstore_flash_fsm_state_reading, ctx); + } +} + +/* static void cfstore_flash_fsm_init_on_exit(void* context){ (void) context;} */ + +/* brief callback handler called when entering the reading state + * note + * flash journal has initialised successfully. now + */ +static void cfstore_flash_fsm_read_on_entry(void* context) +{ + uint8_t* ptr = NULL; + int32_t ret = 0; + FlashJournal_Info_t info; + FlashJournal_Status_t status = JOURNAL_STATUS_ERROR; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_ASSERT(ctx != NULL); + /* drv->GetInfo() is synchronous */ + status = FlashJournal_getInfo(&ctx->jrnl, &info); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: failed get journal info (status=%d)\r\n", __func__, (int) status); + TEST_ASSERT_MESSAGE(status == JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + CFSTORE_DBGLOG("%s:FlashJournal_getInfo() done. info.sizeofJournaledBlob=%lu\r\n", __func__, (long unsigned int) info.sizeofJournaledBlob); + + if(info.sizeofJournaledBlob > 0) + { + /* setup the expected blob size for writing + * This is a multiple of program unit so the write doesnt fail due to unaligned log */ + ctx->expected_blob_size = sizeof(cfstore_flash_data_blob_t); + if(ctx->expected_blob_size % CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE > 0){ + ctx->expected_blob_size += (CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE - (sizeof(cfstore_flash_data_blob_t) % CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE)); + } + /* test that a blob size is the expected size for flash data that has been written before */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: info.sizeofJournaledBlob does not match expect size. expected_blob_size (%lu) != info.sizeofJournaledBlob (%lu)\r\n", __func__, (unsigned long int) ctx->expected_blob_size, (unsigned long int) info.sizeofJournaledBlob); + TEST_ASSERT_EQUAL_INT64_MESSAGE(ctx->expected_blob_size, info.sizeofJournaledBlob, cfstore_flash_utest_msg_g); + + /* grow the area by the size of the stored blob */ + ptr = (uint8_t*) CFSTORE_REALLOC((void*) ctx->area_0_head, ctx->expected_blob_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:realloc failed flash blob (size=%d)\r\n", __func__, (int)info.sizeofJournaledBlob); + TEST_ASSERT_MESSAGE(ptr != NULL, cfstore_flash_utest_msg_g); + memset(ptr, 0, ctx->expected_blob_size); + if(ptr != ctx->area_0_head){ + CFSTORE_DBGLOG("%s:cfstore_ctx_g.area_0_head pointer changed (cfstore_ctx_g.area_0_head=%p, ptr=%p)\r\n", __func__, ctx->area_0_head, ptr); + ctx->area_0_head = ptr; + ctx->area_0_tail = ctx->area_0_head + info.sizeofJournaledBlob; + } + ret = FlashJournal_read(&ctx->jrnl, (void*) ctx->area_0_head, info.sizeofJournaledBlob); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to read flash journal (ret=%d. info.sizeofJournaledBlob=%d)\r\n", __func__, (int) ret, (int) info.sizeofJournaledBlob); + TEST_ASSERT_MESSAGE(ret >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + if(ret > 0){ + /* read has completed synchronously*/ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to read all data from flash journal (expected size=%lu, read=%d)\r\n", __func__, (unsigned long int)info.sizeofJournaledBlob, (int) ret); + TEST_ASSERT_EQUAL_INT32_MESSAGE( (int32_t) info.sizeofJournaledBlob, ret, cfstore_flash_utest_msg_g); + cfstore_flash_test_01_callback(ret, FLASH_JOURNAL_OPCODE_READ_BLOB); + } + } else { + /* there is no blob, move to next state. need a +ve status value to indicate async completion + * to the fsm reading state handler. use CFSTORE_FLASH_AREA_SIZE_MIN for this value */ + ctx->expected_blob_size = CFSTORE_FLASH_AREA_SIZE_MIN; + status = (FlashJournal_Status_t) CFSTORE_FLASH_AREA_SIZE_MIN; + cfstore_flash_test_01_callback(status, FLASH_JOURNAL_OPCODE_READ_BLOB); + } + return; +} + +/* @brief fsm handler when in reading state */ +void cfstore_flash_fsm_reading(void* context) +{ + int32_t ret = 0; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + cfstore_flash_data_blob_t *blob = NULL; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in reading state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flash_fsm_state_reading, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, FLASH_JOURNAL_OPCODE_READ_BLOB); + TEST_ASSERT_MESSAGE(ctx->cmd_code == FLASH_JOURNAL_OPCODE_READ_BLOB, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%d\r\n", __func__, (int) ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + + if(ctx->status > 0) + { + if(ctx->status > (int32_t) CFSTORE_FLASH_AREA_SIZE_MIN) + { + /* check the correct amount of data was read, which is the status code */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to read all data from flash journal (expected size=%lu, read=%d)\r\n", __func__, (unsigned long int) ctx->expected_blob_size, (int) ctx->status); + /* ctx->status contains the status of the read that was completed */ + TEST_ASSERT_EQUAL_INT64_MESSAGE(ctx->expected_blob_size, (int32_t) ctx->status, cfstore_flash_utest_msg_g); + if(ctx->area_0_head != NULL) + { + /* check the key_name read from flash is correct */ + blob = (cfstore_flash_data_blob_t*) ctx->area_0_head; + cfstore_dump_key_name(blob->data, CFSTORE_TEST_DATA_KEYNAME_SIZE, __func__); + ret = memcmp(blob->data, cfstore_flush_test_01_kv_data[0].key_name, strlen(cfstore_flush_test_01_kv_data[0].key_name)); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: incorrect key_name read from flash (expected 0 from memcpy(keyname, flash_data), actual was non-zero)", __func__); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, ret, cfstore_flash_utest_msg_g); + } + } + cfstore_fsm_state_set(&ctx->fsm, cfstore_flash_fsm_state_writing, ctx); + } +} + +/* void cfstore_flash_fsm_read_on_exit(void* context){ (void) context;} */ + +/* @brief on entry to writing state, update value */ +void cfstore_flash_fsm_write_on_entry(void* context) +{ + uint8_t value = 0; + int32_t ret = 0; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + cfstore_flash_data_blob_t *blob = NULL; + + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + /* allocate memory for data if not already done so */ + if(ctx->area_0_head == NULL) + { + /* This is a multiple of program unit so the write doesnt fail due to unaligned log */ + ctx->expected_blob_size = sizeof(cfstore_flash_data_blob_t); + if(ctx->expected_blob_size % CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE > 0){ + ctx->expected_blob_size += (CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE - (sizeof(cfstore_flash_data_blob_t) % CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE)); + } + ctx->area_0_head = (uint8_t*) CFSTORE_REALLOC((void*) ctx->area_0_head, ctx->expected_blob_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to allocate memory for context\r\n", __func__); + TEST_ASSERT_MESSAGE(ctx->area_0_head != NULL, cfstore_flash_utest_msg_g); + ctx->area_0_tail = ctx->area_0_head + ctx->expected_blob_size; + memset(ctx->area_0_head, 0, ctx->expected_blob_size); + /* setup data to write to flash */ + blob = (cfstore_flash_data_blob_t*) ctx->area_0_head; + blob->hdr.klength = strlen(cfstore_flush_test_01_kv_data->key_name); + blob->hdr.vlength= 1; + blob->hdr.perm_owner_read = true; + blob->hdr.perm_owner_write = true; + blob->hdr.refcount = 1; + memcpy((void*) blob->data, (const void*) cfstore_flush_test_01_kv_data->key_name, strlen(cfstore_flush_test_01_kv_data->key_name)); + memcpy((void*) &blob->data[CFSTORE_TEST_DATA_KEYNAME_SIZE], (const void*) cfstore_flush_test_01_kv_data->value, strlen(cfstore_flush_test_01_kv_data->value)); + } + if(ctx->area_0_head != NULL) + { + /* data has been read */ + /* check the key_name read from flash is correct */ + blob = (cfstore_flash_data_blob_t*) ctx->area_0_head; + value = (uint8_t) blob->data[CFSTORE_TEST_DATA_KEYNAME_SIZE]; + CFSTORE_DBGLOG("INFO: value read from flash = %u\r\n", value); + /* update the value */ + value++; + memcpy((void*) &blob->data[CFSTORE_TEST_DATA_KEYNAME_SIZE], (const void*) &value, sizeof(uint8_t)); + } + + ret = FlashJournal_log(&ctx->jrnl, (const void*) ctx->area_0_head, ctx->expected_blob_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: ret = JOURNAL_STATUS_SMALL_LOG_REQUEST, bailing out.", __func__); + TEST_ASSERT_MESSAGE(ret != (int32_t) JOURNAL_STATUS_SMALL_LOG_REQUEST, cfstore_flash_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform log operation to flash journal (ret=%d)\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, cfstore_flash_utest_msg_g); + if(ret > 0){ + /* write has completed synchronously*/ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to write all data from flash journal (expected size=%lu, read=%d)\r\n", __func__, (unsigned long int) ctx->expected_blob_size, (int) ret); + TEST_ASSERT_EQUAL_INT32_MESSAGE(ret, (int32_t) ctx->expected_blob_size, cfstore_flash_utest_msg_g); + cfstore_flash_test_01_callback(ret, FLASH_JOURNAL_OPCODE_LOG_BLOB); + } + /* wait for async completion handler*/ +} + +/* @brief fsm handler when in reading state */ +void cfstore_flash_fsm_writing(void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in writing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flash_fsm_state_writing, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, FLASH_JOURNAL_OPCODE_LOG_BLOB); + TEST_ASSERT_MESSAGE(ctx->cmd_code == FLASH_JOURNAL_OPCODE_LOG_BLOB, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%d\r\n", __func__, (int) ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + + if(ctx->status > 0){ + /* check the correct amount of data was written */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to write all data from flash journal (expected size=%lu, read=%d)\r\n", __func__, (unsigned long int) ctx->expected_blob_size, (int) ctx->status); + TEST_ASSERT_EQUAL_INT64_MESSAGE(ctx->expected_blob_size, ctx->status, cfstore_flash_utest_msg_g); + cfstore_fsm_state_set(&ctx->fsm, cfstore_flash_fsm_state_committing, ctx); + } + return; +} + +/* void cfstore_flash_fsm_write_on_exit(void* ctx){(void) ctx;} */ + +/* @brief fsm handler when entering committing state */ +void cfstore_flash_fsm_commit_on_entry(void* context) +{ + int32_t ret = 0; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + ret = FlashJournal_commit(&ctx->jrnl); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform commit operation to flash journal (ret=%d)\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= (int32_t) JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + /* for flash-journal-strategy-sequential version >0.4.0, commit() return no longer reports size of commit block */ + if(ret > 0){ + Harness::validate_callback(); + } + /* wait for async completion handler*/ + return; +} + + +/* @brief fsm handler when in committing state */ +void cfstore_flash_fsm_committing(void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in committing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flash_fsm_state_committing, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, FLASH_JOURNAL_OPCODE_COMMIT); + TEST_ASSERT_MESSAGE(ctx->cmd_code == FLASH_JOURNAL_OPCODE_COMMIT, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%d\r\n", __func__, (int) ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + + /* check the correct amount of data was written */ + /* for flash-journal-strategy-sequential version >0.4.0, commit() return no longer reports size of commit block */ + Harness::validate_callback(); +} + +/* @brief fsm handler when exiting committing state */ +void cfstore_flash_fsm_commit_on_exit(void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + /* test done. clean up*/ + if(ctx->area_0_head){ + CFSTORE_FREE(ctx->area_0_head); + ctx->area_0_head = NULL; + } + Harness::validate_callback(); + } + +#define cfstore_flash_fsm_null NULL + +/* handler functions while in state */ +static cfstore_flash_fsm_handler cfstore_flash_fsm[cfstore_flash_fsm_state_max][cfstore_flash_fsm_event_max] = +{ +/* state\event: init_done read_done write_done commit_done */ +/* initialising */ {cfstore_flash_fsm_initializing, cfstore_flash_fsm_null, cfstore_flash_fsm_null, cfstore_flash_fsm_null }, +/* reading */ {cfstore_flash_fsm_null, cfstore_flash_fsm_reading, cfstore_flash_fsm_null, cfstore_flash_fsm_null }, +/* writing */ {cfstore_flash_fsm_null, cfstore_flash_fsm_null, cfstore_flash_fsm_writing, cfstore_flash_fsm_null }, +/* committing */ {cfstore_flash_fsm_null, cfstore_flash_fsm_null, cfstore_flash_fsm_null, cfstore_flash_fsm_committing }, +}; + + +/* handler functions for entering the state*/ +cfstore_flash_fsm_handler cfstore_flash_fsm_on_entry[cfstore_flash_fsm_state_max] = +{ + cfstore_flash_fsm_init_on_entry, + cfstore_flash_fsm_read_on_entry, + cfstore_flash_fsm_write_on_entry, + cfstore_flash_fsm_commit_on_entry, +}; + +/* handler functions for exiting state, currently none used */ +cfstore_flash_fsm_handler cfstore_flash_fsm_on_exit[cfstore_flash_fsm_state_max] = +{ + NULL, NULL, NULL, NULL, +}; + + +/* @brief inject event into fsm */ +static void cfstore_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_flash_fsm_event_t event, void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, event=%d (%s), ctx=%p\r\n", __func__, fsm, (int) event, cfstore_flash_event_str[event], ctx); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: invalid event (%d)\r\n", __func__, (int) event); + TEST_ASSERT_MESSAGE(event < cfstore_flash_fsm_event_max, cfstore_flash_utest_msg_g); + fsm->event = event; + if(cfstore_flash_fsm[fsm->state][fsm->event] != NULL){ + cfstore_flash_fsm[fsm->state][fsm->event](ctx); + } + + /* do not clear context data set by caller as it may be used later + * fsm->event = cfstore_flash_fsm_event_max; + * ctx->status = 0; + * ctx->cmd_code = (FlashJournal_OpCode_t)((int) FLASH_JOURNAL_OPCODE_RESET+1); + */ + return; +} + + +/* @brief function to move to new fsm state, calling state exit function for old state and entry function for new state */ +static void cfstore_fsm_state_set(cfstore_fsm_t* fsm, cfstore_flash_fsm_state_t new_state, void* ctx) +{ + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, new_state=%d, ctx=%p\r\n", __func__, fsm, (int) new_state, ctx); + CFSTORE_DBGLOG("%s:FSM:REQ RX:%s:%s\r\n", __func__, cfstore_flash_state_str[fsm->state], cfstore_flash_state_str[new_state]); + TEST_ASSERT_MESSAGE(fsm != NULL, "fsm is not a valid pointer"); + TEST_ASSERT_MESSAGE(new_state < cfstore_flash_fsm_state_max, "new_state is not a valid state"); + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is not a valid pointer"); + TEST_ASSERT_MESSAGE(fsm->state < cfstore_flash_fsm_state_max, "fsm->state is not a valid state"); + + if(cfstore_flash_fsm_on_exit[fsm->state] != NULL){ + cfstore_flash_fsm_on_exit[fsm->state](ctx); + } + fsm->state = new_state; + if(cfstore_flash_fsm_on_entry[new_state] != NULL){ + cfstore_flash_fsm_on_entry[new_state](ctx); + } + CFSTORE_DBGLOG("%s:FSM:REQ DONE:\r\n", __func__); + return; +} + +/* @brief initialize global context data structure */ +static void cfstore_flash_ctx_init(cfstore_flash_ctx_t* ctx) +{ + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is NULL"); + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&cfstore_flash_ctx_g, 0, sizeof(cfstore_flash_ctx_g)); +} + + +/* @brief asynchronous test 01 */ +static control_t cfstore_flash_journal_async_test_01(void) +{ + cfstore_flash_ctx_t* ctx = cfstore_flash_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + cfstore_flash_mtd_async_ops_g = CFSTORE_FLASH_MTD_ASYNC_OPS_ON; + cfstore_flash_ctx_init(ctx); + cfstore_fsm_state_set(&ctx->fsm, cfstore_flash_fsm_state_initializing, ctx); + + /* allow time for work to be done */ + return CaseTimeout(CFSTORE_FLASH_CASE_TIMEOUT_MS); +} + +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_flash_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flash_utest_msg_g); + +#ifndef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED + CFSTORE_LOG("INITIALIZING: BACKEND=SRAM. Skipping flash test%s", "\n"); +#endif + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +/* Specify all your test cases here */ +Case cases[] = { + Case("flash_journal_async_test_00", cfstore_flash_test_00), +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED + Case("flash_journal_async_test_01", cfstore_flash_journal_async_test_01), +#endif +}; + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash_set/flash_set.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash_set/flash_set.cpp new file mode 100644 index 0000000000..74119b49b1 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash_set/flash_set.cpp @@ -0,0 +1,116 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** @file flash_set.cpp Test tool to set flash to some data + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "Driver_Common.h" +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +/* Create the main box ACL list for the application. + * The main ACL gets inherited by all the other boxes + */ +CFSTORE_UVISOR_MAIN_ACL(cfstore_acl_uvisor_box_flash_set_g); + +/* Enable uVisor. */ +UVISOR_SET_MODE_ACL(UVISOR_ENABLED, cfstore_acl_uvisor_box_flash_set_g); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + + +/** + * @brief add ~50 KVs and store them in flash + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t cfstore_flash_set_test_01_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_CAPABILITIES caps = cfstore_driver.GetCapabilities(); + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&flags, 0, sizeof(flags)); + + CFSTORE_LOG("caps.asynchronous_ops : %d\n", (int) caps.asynchronous_ops); + ret = cfstore_test_init_1(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to write data to falsh.", __func__); + } + ret = drv->Flush(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Flush() call failed (ret=%d).\r\n", __func__, (int) ret); + } +#ifdef CFSTORE_DEBUG + ret = cfstore_test_dump(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("Error: failed to dump CFSTORE contents%s", "\n"); + } +#endif /* CFSTORE_DEBUG */ + ret = drv->Uninitialize(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("Error: failed to Uninitialize() CFSTORE%s", "\n"); + } + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FLASH_SET_test_01_start", cfstore_utest_default_start), + Case("FLASH_SET_test_01_end", cfstore_flash_set_test_01_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush/flush.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush/flush.cpp new file mode 100644 index 0000000000..3aa9da5baa --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush/flush.cpp @@ -0,0 +1,630 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file flush.cpp Test cases to flush KVs in the CFSTORE using the drv->Flush() interface. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include "mbed.h" +#include "cfstore_config.h" +#include "Driver_Common.h" +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +static control_t cfstore_flush_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("%s:Not implemented for ARM toolchain\n", __func__); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FLUSH_test_00", cfstore_flush_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + +#else + + + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +/* + * Defines + */ +/// @cond CFSTORE_DOXYGEN_DISABLE +#define CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE 256 +#define cfstore_flush_fsm_null NULL +#define CFSTORE_FLUSH_CASE_TIMEOUT_MS 10000 +#define CFSTORE_FLUSH_FSM_LOOPS 20 + +#ifdef CFSTORE_DEBUG +#define CFSTORE_FLUSH_GREENTEA_TIMEOUT_S 360 +#else +#define CFSTORE_FLUSH_GREENTEA_TIMEOUT_S 60 +#endif + +/* + * Globals + * + * cfstore_flush_utest_msg_g + * buffer for storing TEST_ASSERT_xxx_MESSAGE messages + */ +char cfstore_flush_utest_msg_g[CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.flush.box1"); +UVISOR_BOX_CONFIG(cfstore_flush_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + + + +#ifdef TARGET_LIKE_X86_LINUX_NATIVE + +/** @brief basic Flush() test + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +int32_t cfstore_flush_test_01_x86_sync(void) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + ret = drv->Initialize(NULL, NULL); + if(ret != ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Initialize() call failed (ret=%d).\r\n", __func__, (int) ret); + goto out0; + } + ret = drv->Flush(); + if(ret != ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Flush() call failed (ret=%d).\r\n", __func__, (int) ret); + } + ret = drv->Uninitialize(); + if(ret != ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Initialize() call failed to Uninitialise(ret=%d).\r\n", __func__, (int) ret); + goto out0; + } + out0: + return ret; +} +#endif /* TARGET_LIKE_X86_LINUX_NATIVE */ + +/* KV data for test_03 */ +static cfstore_kv_data_t cfstore_flush_test_02_kv_data[] = { + { "com.arm.mbed.configurationstore.test.flush.cfstoreflushtest02", "1"}, + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + { NULL, NULL}, +}; + + +/* async test version */ + +/* @brief test fsm states and events */ +typedef enum cfstore_flush_fsm_state_t { + cfstore_flush_fsm_state_stopped = 0, + cfstore_flush_fsm_state_initializing, + cfstore_flush_fsm_state_flushing, + cfstore_flush_fsm_state_uninitializing, + cfstore_flush_fsm_state_max +} cfstore_flush_fsm_state_t; + +/* @brief test fsm events */ +typedef enum cfstore_flush_fsm_event_t { + cfstore_flush_fsm_event_init_done = 0, + cfstore_flush_fsm_event_flush_done, + cfstore_flush_fsm_event_uninit_done, + cfstore_flush_fsm_event_max, +} cfstore_flush_fsm_event_t; + +typedef void (*cfstore_flush_fsm_handler)(void* ctx); + +/// @cond CFSTORE_DOXYGEN_DISABLE +typedef struct cfstore_fsm_t +{ + cfstore_flush_fsm_state_t state; + cfstore_flush_fsm_event_t event; +} cfstore_fsm_t; +/// @endcond + +/// @cond CFSTORE_DOXYGEN_DISABLE +typedef struct cfstore_flush_ctx_t +{ + int32_t loops_done; + cfstore_fsm_t fsm; + int32_t status; + ARM_CFSTORE_OPCODE cmd_code; +} cfstore_flush_ctx_t; +/// @endcond + + +/* + * Globals + */ +static cfstore_flush_ctx_t cfstore_flush_ctx_g; + +#ifdef CFSTORE_DEBUG +static const char* cfstore_flush_state_str[] = +{ + "stopped", + "initializing", + "flushing", + "uninitializing", + "unknown" +}; + +static const char* cfstore_flush_event_str[] = +{ + "init_done", + "flush_done", + "uninit_done", + "unknown" +}; +#endif + +/// @endcond + +/* + * Forward decl + */ +static void cfstore_flush_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_flush_fsm_event_t event, void* context); +static void cfstore_flush_fsm_state_set(cfstore_fsm_t* fsm, cfstore_flush_fsm_state_t new_state, void* ctx); +/* + * context related methods + */ + +/* @brief get a pointer to the global context data structure */ +static cfstore_flush_ctx_t* cfstore_flush_ctx_get(void) +{ + return &cfstore_flush_ctx_g; +} + +/* @brief initialize global context data structure */ +static void cfstore_flush_ctx_init(cfstore_flush_ctx_t* ctx) +{ + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is NULL"); + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&cfstore_flush_ctx_g, 0, sizeof(cfstore_flush_ctx_g)); +} + + +/* @brief cfstore asynchronous callback handler */ +void cfstore_flush_test_01_callback(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) client_context; + + CFSTORE_FENTRYLOG("%s:entered: status=%d, cmd_code=%d (%s) ctx=%p, handle=%p\r\n", __func__, (int) status, (int) cmd_code, cfstore_test_opcode_str[cmd_code], ctx, handle); + (void) handle; + switch(cmd_code) + { + case CFSTORE_OPCODE_INITIALIZE: + ctx->fsm.event = cfstore_flush_fsm_event_init_done; + break; + case CFSTORE_OPCODE_FLUSH: + ctx->fsm.event = cfstore_flush_fsm_event_flush_done; + break; + case CFSTORE_OPCODE_UNINITIALIZE: + ctx->fsm.event = cfstore_flush_fsm_event_uninit_done; + break; + case CFSTORE_OPCODE_CLOSE: + case CFSTORE_OPCODE_CREATE: + case CFSTORE_OPCODE_DELETE: + case CFSTORE_OPCODE_FIND: + case CFSTORE_OPCODE_GET_KEY_NAME: + case CFSTORE_OPCODE_GET_STATUS: + case CFSTORE_OPCODE_GET_VALUE_LEN: + case CFSTORE_OPCODE_OPEN: + case CFSTORE_OPCODE_POWER_CONTROL: + case CFSTORE_OPCODE_READ: + case CFSTORE_OPCODE_RSEEK: + case CFSTORE_OPCODE_WRITE: + default: + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:WARN: received asynchronous notification for opcode=%d (%s) when api call should have been synchronous", __func__, cmd_code, cmd_code < CFSTORE_OPCODE_MAX ? cfstore_test_opcode_str[cmd_code] : "unknown"); + CFSTORE_DBGLOG("%s:WARN: received asynchronous notification for opcode=%d (%s) when api call should have been synchronous", __func__, cmd_code, cmd_code < CFSTORE_OPCODE_MAX ? cfstore_test_opcode_str[cmd_code] : "unknown"); + /* TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g) */ + return; + } + ctx->status = status; + ctx->cmd_code = cmd_code; + cfstore_flush_fsm_state_handle_event(&ctx->fsm, ctx->fsm.event, client_context); + return; +} + +/* @brief fsm handler called on entry to stopping state */ +static void cfstore_flush_fsm_stop_on_entry(void* context) +{ + (void) context; + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + Harness::validate_callback(); + return; +} + +/* @brief fsm handler called on entry to initializing state */ +static void cfstore_flush_fsm_init_on_entry(void* context) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + const ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: callback=%p, ctx=%p\r\n", __func__, cfstore_flush_test_01_callback, ctx); + ret = drv->Initialize(cfstore_flush_test_01_callback, ctx); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%d)\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + CFSTORE_DBGLOG("%s:debug: ret=%d\r\n", __func__, (int) ret); + return; +} + +/* brief callback handler when in state initializing */ +static void cfstore_flush_fsm_initializing(void* context) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_FENTRYLOG("%s:entered: status = %d\r\n", __func__, (int) ctx->status); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in initializing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_initializing, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, CFSTORE_OPCODE_INITIALIZE); + TEST_ASSERT_MESSAGE(ctx->cmd_code == CFSTORE_OPCODE_INITIALIZE, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%d\r\n", __func__, (int) ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* only change state if status >= 0*/ + if(ctx->status >= 0){ + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_flushing, ctx); + } + return; +} + +/* static void cfstore_flush_fsm_init_on_exit(void* context){ (void) context;} */ + + +/* @brief fsm handler called on entry to flushing state */ +static void cfstore_flush_fsm_flush_on_entry(void* context) +{ + bool bfound = false; + int32_t ivalue = 0; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + const char* key_name_query = "*"; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + ARM_CFSTORE_SIZE len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ARM_CFSTORE_HANDLE_INIT(next); + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_KEYDESC kdesc; + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in flushing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_flushing, cfstore_flush_utest_msg_g); + /* try to read key; should not be found */ + ret = cfstore_test_kv_is_found(cfstore_flush_test_02_kv_data->key_name, &bfound); + if(ret != ARM_DRIVER_OK && ret != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cfstore_test_kv_is_found() call failed (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g); + } + + if(!bfound) + { + /* first time start up. nothing is stored in cfstore flash. check this is the case */ + while(drv->Find(key_name_query, prev, next) == ARM_DRIVER_OK) + { + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: have found an entry in cfstore when none should be present", __func__); + TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g); + } + /* no entries found, which is correct. + * store a value */ + len = strlen(cfstore_flush_test_02_kv_data->value); + ret = cfstore_test_create(cfstore_flush_test_02_kv_data->key_name, cfstore_flush_test_02_kv_data->value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error:1: failed to write kv data (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* flush to flash */ + ret = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to flush data to cfstore flash (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("FLUSH: Success pending for new KV creation (name=%s, value=%s)\n", cfstore_flush_test_02_kv_data->key_name, cfstore_flush_test_02_kv_data->value); + } else { + /*read the value, increment by 1 and write value back */ + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = cfstore_test_read(cfstore_flush_test_02_kv_data->key_name, value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read kv data (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + ivalue = atoi(value); + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("FLUSH: Read KV from flash (name=%s, value=%d)\n", cfstore_flush_test_02_kv_data->key_name, (int) ivalue); + /* increment value */ + ++ivalue; + snprintf(value, CFSTORE_KEY_NAME_MAX_LENGTH+1, "%d", (int) ivalue); + len = strlen(value); + ret = cfstore_test_write(cfstore_flush_test_02_kv_data->key_name, value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write kv data (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + /* flush to flash */ + ret = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to flush data to cfstore flash (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("FLUSH: Success pending for new KV value to flash (name=%s, value=%d)\n", cfstore_flush_test_02_kv_data->key_name, (int) ivalue); + } + return; +} + + +/* brief callback handler when in state flushing */ +static void cfstore_flush_fsm_flushing(void* context) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in flushing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_flushing, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, CFSTORE_OPCODE_FLUSH); + TEST_ASSERT_MESSAGE(ctx->cmd_code == CFSTORE_OPCODE_FLUSH, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%d\r\n", __func__, (int) ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* only change state if status >= 0*/ + if(ctx->status >= 0){ + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("FLUSH: Successfully flushed data to flash.%s", "\n"); + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_uninitializing, ctx); + } + return; +} + +/* static void cfstore_flush_fsm_flush_on_exit(void* context){ (void) context;} */ + + +/* @brief fsm handler called on entry to uninitializing state */ +static void cfstore_flush_fsm_uninit_on_entry(void* context) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + const ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in uninitializing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_uninitializing, cfstore_flush_utest_msg_g); + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to uninitialize CFSTORE (ret=%d)\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + return; +} + +/* brief callback handler when in state uninitializing */ +static void cfstore_flush_fsm_uninitializing(void* context) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_DBGLOG("%s:ctx->status=%d, ctx->loops_done=%d\r\n", __func__, (int) ctx->status, (int) ctx->loops_done); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in uninitializing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_uninitializing, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, CFSTORE_OPCODE_UNINITIALIZE); + TEST_ASSERT_MESSAGE(ctx->cmd_code == CFSTORE_OPCODE_UNINITIALIZE, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%d\r\n", __func__, (int) ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* only change state if status >= 0*/ + if(ctx->status >= ARM_DRIVER_OK){ + ++ctx->loops_done; + if(ctx->loops_done < CFSTORE_FLUSH_FSM_LOOPS){ + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_initializing, ctx); + } else { + /* move to init state */ + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_stopped, ctx); + } + } + return; +} + +/* static void cfstore_flush_fsm_uninit_on_exit(void* context) {(void)context;} */ + + + +/* handler functions while in state */ +static cfstore_flush_fsm_handler cfstore_flush_fsm[cfstore_flush_fsm_state_max][cfstore_flush_fsm_event_max] = +{ +/* state\event: init_done flush_done unint_done */ +/* stopped */ {cfstore_flush_fsm_null, cfstore_flush_fsm_null, cfstore_flush_fsm_null }, +/* initialising */ {cfstore_flush_fsm_initializing, cfstore_flush_fsm_null, cfstore_flush_fsm_null }, +/* flushing */ {cfstore_flush_fsm_null, cfstore_flush_fsm_flushing, cfstore_flush_fsm_null }, +/* uninitializing */ {cfstore_flush_fsm_null, cfstore_flush_fsm_null, cfstore_flush_fsm_uninitializing } +}; + + +/* handler functions for entering the state*/ +static cfstore_flush_fsm_handler cfstore_flush_fsm_on_entry[cfstore_flush_fsm_state_max] = +{ + cfstore_flush_fsm_stop_on_entry, + cfstore_flush_fsm_init_on_entry, + cfstore_flush_fsm_flush_on_entry, + cfstore_flush_fsm_uninit_on_entry +}; + +/* handler functions for exiting state, currently none used */ +static cfstore_flush_fsm_handler cfstore_flush_fsm_on_exit[cfstore_flush_fsm_state_max] = +{ + cfstore_flush_fsm_null, + cfstore_flush_fsm_null, + cfstore_flush_fsm_null, + cfstore_flush_fsm_null +}; + + +/* @brief inject event into fsm */ +static void cfstore_flush_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_flush_fsm_event_t event, void* context) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, state=(%s), event=%d (%s), ctx=%p\r\n", __func__, fsm, cfstore_flush_state_str[fsm->state], (int) event, cfstore_flush_event_str[event], ctx); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: invalid event (%d)\r\n", __func__, (int) event); + TEST_ASSERT_MESSAGE(event < cfstore_flush_fsm_event_max, cfstore_flush_utest_msg_g); + fsm->event = event; + if(cfstore_flush_fsm[fsm->state][fsm->event] != NULL){ + cfstore_flush_fsm[fsm->state][fsm->event](ctx); + } + + /* do not clear context data set by caller as it may be used later + * fsm->event = cfstore_flush_fsm_event_max; + * ctx->status = 0; + * ctx->cmd_code = (FlashJournal_OpCode_t)((int) FLASH_JOURNAL_OPCODE_RESET+1); + */ + return; +} + + +/* @brief function to move to new fsm state, calling state exit function for old state and entry function for new state */ +static void cfstore_flush_fsm_state_set(cfstore_fsm_t* fsm, cfstore_flush_fsm_state_t new_state, void* ctx) +{ + #ifdef CFSTORE_DEBUG + cfstore_flush_fsm_state_t old_state = fsm->state; + #endif + + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, ctx=%p\r\n", __func__, fsm, ctx); + #ifdef CFSTORE_DEBUG + CFSTORE_DBGLOG("%s:FSM:REQ RX:%s:%s\r\n", __func__, cfstore_flush_state_str[fsm->state], cfstore_flush_state_str[new_state]); + #endif + TEST_ASSERT_MESSAGE(fsm != NULL, "fsm is not a valid pointer"); + TEST_ASSERT_MESSAGE(new_state < cfstore_flush_fsm_state_max, "new_state is not a valid state"); + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is not a valid pointer"); + TEST_ASSERT_MESSAGE(fsm->state < cfstore_flush_fsm_state_max, "fsm->state is not a valid state"); + + if(cfstore_flush_fsm_on_exit[fsm->state] != NULL){ + cfstore_flush_fsm_on_exit[fsm->state](ctx); + } + fsm->state = new_state; + if(cfstore_flush_fsm_on_entry[new_state] != NULL){ + cfstore_flush_fsm_on_entry[new_state](ctx); + } + #ifdef CFSTORE_DEBUG + CFSTORE_DBGLOG("%s:FSM:REQ DONE fsm=%p, fsm->state (old_state)=%s, new_state=%s, ctx=%p\r\n", __func__, fsm, cfstore_flush_state_str[old_state], cfstore_flush_state_str[new_state], ctx); + #endif + return; +} + + +/* @brief asynchronous test 01 */ +static control_t cfstore_flush_test_02_k64f(void) +{ + cfstore_flush_ctx_t* ctx = cfstore_flush_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + cfstore_flush_ctx_init(ctx); + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_initializing, ctx); + return CaseTimeout(CFSTORE_FLUSH_GREENTEA_TIMEOUT_S*1000); +} + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_flush_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(CFSTORE_FLUSH_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("FLUSH_test_00", cfstore_flush_test_00), + Case("FLUSH_test_01", cfstore_flush_test_02_k64f), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush2/flush2.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush2/flush2.cpp new file mode 100644 index 0000000000..3d1dd02fbd --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush2/flush2.cpp @@ -0,0 +1,279 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file flush2.cpp Test cases to flush KVs in the CFSTORE using the drv->Flush() interface. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "cfstore_config.h" +#include "configuration_store.h" +#include "greentea-client/test_env.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +/// @cond CFSTORE_DOXYGEN_DISABLE +/* + * Defines + */ +#define CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE 256 +#define cfstore_flush_fsm_null NULL +#define CFSTORE_FLUSH_CASE_TIMEOUT_MS 10000 + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.flush2.box1"); +UVISOR_BOX_CONFIG(cfstore_flush2_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* + * Globals + * + * cfstore_flush_utest_msg_g + * buffer for storing TEST_ASSERT_xxx_MESSAGE messages + */ +static char cfstore_flush_utest_msg_g[CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE]; + +/* KV data for test_03 */ +static cfstore_kv_data_t cfstore_flush_test_02_kv_data[] = { + { "com.arm.mbed.configurationstore.test.flush.cfstoreflushtest02", "1"}, + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + { NULL, NULL}, +}; + +/* async test version */ + +typedef struct cfstore_flush_ctx_t +{ + int32_t status; + ARM_CFSTORE_OPCODE cmd_code; +} cfstore_flush_ctx_t; +/// @endcond + +/* + * Globals + */ +static cfstore_flush_ctx_t cfstore_flush_ctx_g; + +/* + * context related methods + */ + +/* @brief get a pointer to the global context data structure */ +static cfstore_flush_ctx_t* cfstore_flush_ctx_get(void) +{ + return &cfstore_flush_ctx_g; +} + +/* @brief initialize global context data structure */ +static void cfstore_flush_ctx_init(cfstore_flush_ctx_t* ctx) +{ + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is NULL"); + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&cfstore_flush_ctx_g, 0, sizeof(cfstore_flush_ctx_g)); +} + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_flush2_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + return CaseNext; +} + +/** + * @brief + * + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_flush2_test_01_start(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_flush_ctx_t* ctx = cfstore_flush_ctx_get(); + const ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + (void) call_count; + cfstore_flush_ctx_init(ctx); + ret = drv->Initialize(cfstore_utest_default_callback, ctx); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%d)\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + return CaseTimeout(100000); +} + + +/** + * @brief + * + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_flush2_test_01_mid(const size_t call_count) +{ + + bool bfound = false; + int32_t ivalue = 0; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + const char* key_name_query = "*"; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + ARM_CFSTORE_SIZE len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ARM_CFSTORE_HANDLE_INIT(next); + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_KEYDESC kdesc; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + + /* try to read key; should not be found */ + ret = cfstore_test_kv_is_found(cfstore_flush_test_02_kv_data->key_name, &bfound); + if(ret != ARM_DRIVER_OK && ret != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cfstore_test_kv_is_found() call failed (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g); + } + + if(!bfound) + { + /* first time start up. nothing is stored in cfstore flash. check this is the case */ + while(drv->Find(key_name_query, prev, next) == ARM_DRIVER_OK) + { + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: have found an entry in cfstore when none should be present", __func__); + TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g); + } + /* no entries found, which is correct. + * store a value */ + len = strlen(cfstore_flush_test_02_kv_data->value); + ret = cfstore_test_create(cfstore_flush_test_02_kv_data->key_name, cfstore_flush_test_02_kv_data->value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error:1: failed to write kv data (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* flush to flash */ + ret = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to flush data to cfstore flash (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + } else { + /*read the value, increment by 1 and write value back */ + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = cfstore_test_read(cfstore_flush_test_02_kv_data->key_name, value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read kv data (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + /* increment value */ + ivalue = atoi(value); + ++ivalue; + snprintf(value, CFSTORE_KEY_NAME_MAX_LENGTH+1, "%d", (int) ivalue); + len = strlen(value); + ret = cfstore_test_write(cfstore_flush_test_02_kv_data->key_name, value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write kv data (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + /* flush to flash */ + ret = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to flush data to cfstore flash (ret=%d).\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + } + return CaseTimeout(100000); +} + +/** + * @brief + * + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_flush2_test_01_end(const size_t call_count) +{ + const ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + (void) call_count; + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + return CaseNext; +} + + +/** + * @brief test to open(create) a very large value, write the data, close, then flush. + * for a N keys simultaneously. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_flush2_test_02(const size_t call_count) +{ + (void) call_count; + //todo: implement test + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_DBGLOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_flush_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + // Call the default reporting function- + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("CFSTORE_FLUSH2_test_00", cfstore_flush2_test_00), + Case("CFSTORE_FLUSH2_test_01_start", cfstore_flush2_test_01_start), + Case("CFSTORE_FLUSH2_test_01_mid", cfstore_flush2_test_01_mid), + Case("CFSTORE_FLUSH2_test_01_end", cfstore_flush2_test_01_end), + Case("CFSTORE_FLUSH2_test_02_start", cfstore_utest_default_start), + Case("CFSTORE_FLUSH2_test_02_end", cfstore_flush2_test_02), +}; + + +// Declare your test specification with a custom setup handler +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush3/flush3.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush3/flush3.cpp new file mode 100644 index 0000000000..9313475c31 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush3/flush3.cpp @@ -0,0 +1,886 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file flush3.cpp Test cases to flush KVs in the CFSTORE using the drv->Flush() interface. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + * + * CFSTORE flash-journal sync mode test for github issue Heap Corruption by Jenia Kogan: + * https://github.com/ARMmbed/configuration-store/issues/17 + * No evidence of heap corruption has been found but the test case put together to + * investiage that problem did result in a bug being found, that being that + * when the last attribute was deleted from CFSTORE, this was not properly committed to + * flash, so that CFSTORE was re-initialised, the old attribute was read back from + * flash into the store. This has now been fixed. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "Driver_Common.h" +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_flush3_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +/* Create the main box ACL list for the application. + * The main ACL gets inherited by all the other boxes + */ +CFSTORE_UVISOR_MAIN_ACL(cfstore_acl_uvisor_box_flush3_g); + +/* Enable uVisor. */ +UVISOR_SET_MODE_ACL(UVISOR_ENABLED, cfstore_acl_uvisor_box_flush3_g); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +/// @cond CFSTORE_DOXYGEN_DISABLE +#ifdef CFSTORE_DEBUG +#define CFSTORE_FLUSH3_GREENTEA_TIMEOUT_S 1000 +#else +#define CFSTORE_FLUSH3_GREENTEA_TIMEOUT_S 100 +#endif +/// @endcond + +/* used for sync mode build only */ +#if defined STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS && STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS==0 + +#define CFSTORE_UNUSED_PARAM(param) (void)(param) + +int32_t cfstore_flush3_end(void) +{ + int32_t cfsStatus; + ARM_CFSTORE_DRIVER *cfstoreDriver = &cfstore_driver; + + CFSTORE_DBGLOG("%s:IN\n", __func__); + cfsStatus = cfstoreDriver->Uninitialize(); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("CFStore Finalization failed (err %ld)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + CFSTORE_DBGLOG("%s:OUT:returning ARM_DRIVER_OK\n", __func__); + return ARM_DRIVER_OK; +} + + +int32_t cfstore_flush3_delete_file(const char *fileDir, size_t maxFilePathSize, const char *fileName) +{ + int32_t cfsStatus; + int32_t status = ARM_DRIVER_OK; + + ARM_CFSTORE_DRIVER *cfstoreDriver = &cfstore_driver; + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_HANDLE_INIT(hkey); + + CFSTORE_DBGLOG("%s:IN. File name %s\n", __func__, fileName); + + CFSTORE_UNUSED_PARAM(fileDir); + CFSTORE_UNUSED_PARAM(maxFilePathSize); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Uninitialize Cfstore\n", __func__); + TEST_ASSERT_MESSAGE(cfstoreDriver != NULL, cfstore_flush3_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Invalid file name\n", __func__); + TEST_ASSERT_MESSAGE(fileName != NULL, cfstore_flush3_utest_msg_g); + + memset(&flags, 0, sizeof(flags)); + flags.write = true; + cfsStatus = cfstoreDriver->Open(fileName, flags, hkey); + if (cfsStatus == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + /* printf added: modification to original code supplied by Jenia Kogan. */ + CFSTORE_DBGLOG("%s: cfsStatus == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND. returning success\n", __func__); + return ARM_DRIVER_OK; + } + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Open failed (err %ld)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + cfsStatus = cfstoreDriver->Delete(hkey); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Failed deleting (%s) failed (err %ld)\n", fileName, cfsStatus); + status = ARM_DRIVER_ERROR; + goto out; + } +out: + cfsStatus = cfstoreDriver->Close(hkey); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Close failed (err %ld)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + /* Flash-to-flash only on success */ + if (status == ARM_DRIVER_OK) { + cfsStatus = cfstoreDriver->Flush(); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Flush to flash failed (err %ld)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + } + CFSTORE_DBGLOG("%s:OUT: status=%d\n", __func__, (int) status); + return status; +} + + +int32_t cfstore_flush3_read_file(const char *fileDir, size_t maxFilePathSize, const char *fileName, uint8_t *buff, size_t buffSize) +{ + int32_t cfsStatus; + int32_t status = ARM_DRIVER_OK; + + ARM_CFSTORE_DRIVER *cfstoreDriver = &cfstore_driver; + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_SIZE readCount = buffSize; + ARM_CFSTORE_HANDLE_INIT(hkey); + + CFSTORE_DBGLOG("%s:IN. File name %s, buffer %p, buffsize %d\n", __func__, fileName, buff, buffSize); + + CFSTORE_UNUSED_PARAM(fileDir); + CFSTORE_UNUSED_PARAM(maxFilePathSize); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Uninitialize Cfstore\n", __func__); + TEST_ASSERT_MESSAGE(cfstoreDriver != NULL, cfstore_flush3_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Invalid file name\n", __func__); + TEST_ASSERT_MESSAGE(fileName != NULL, cfstore_flush3_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Invalid buff\n", __func__); + TEST_ASSERT_MESSAGE(buff != NULL, cfstore_flush3_utest_msg_g); + + memset(&flags, 0, sizeof(flags)); + flags.read = true; + + cfsStatus = cfstoreDriver->Open(fileName, flags, hkey); + if(cfsStatus == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_DBGLOG("File (%s) not found (err %ld)\n", fileName, cfsStatus); + return ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND; + + } + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Open failed (err %ld)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + cfsStatus = cfstoreDriver->Read(hkey, buff, &readCount); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Read failed (err %ld)\n", cfsStatus); + status = ARM_DRIVER_ERROR; + goto out; + } + if(readCount < buffSize){ + CFSTORE_DBGLOG("Read failed, amount is %zu while requested %zu\n", readCount, buffSize); + status = ARM_DRIVER_ERROR; + goto out; + } +out: + cfsStatus = cfstoreDriver->Close(hkey); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Close failed (err %ld)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + CFSTORE_DBGLOG("%s:OUT: status=%d\n", __func__, (int) status); + return status; +} + +int32_t cfstore_flush3_write_file(const char *fileDir, size_t maxFilePathSize, const char *fileName, const uint8_t *buff, size_t buffSize) +{ + int32_t cfsStatus; + int32_t status = ARM_DRIVER_ERROR; + + ARM_CFSTORE_DRIVER *cfstoreDriver = &cfstore_driver; + ARM_CFSTORE_SIZE writeCount = buffSize; + ARM_CFSTORE_KEYDESC keyDesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + + CFSTORE_DBGLOG("%s:IN. File name %s, buffer %p, buffsize %d\n", __func__, fileName, buff, buffSize); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Uninitialize Cfstore\n", __func__); + TEST_ASSERT_MESSAGE(cfstoreDriver != NULL, cfstore_flush3_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Invalid file name\n", __func__); + TEST_ASSERT_MESSAGE(fileName != NULL, cfstore_flush3_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Invalid buff\n", __func__); + TEST_ASSERT_MESSAGE(buff != NULL, cfstore_flush3_utest_msg_g); + + /* We always deleting the old file and recreating a new one to preserve simplicity */ + CFSTORE_DBGLOG("Before delete%s", "\n"); + + /* Delete the old file */ + status = cfstore_flush3_delete_file(fileDir, maxFilePathSize, fileName); + if(status != ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Failed deleting (%s)\n", fileName); + return status; + } + + CFSTORE_DBGLOG("After delete%s", "\n"); + /* Create a new file */ + memset(&keyDesc, 0, sizeof(keyDesc)); + keyDesc.drl = ARM_RETENTION_NVM; + keyDesc.flags.read = true; + keyDesc.flags.write = true; + cfsStatus = cfstoreDriver->Create(fileName, buffSize, &keyDesc, hkey); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Fail creating (%s) key-store (%ld)\n", fileName, cfsStatus); + return ARM_DRIVER_ERROR; + } + CFSTORE_DBGLOG("After create%s", "\n"); + cfsStatus = cfstoreDriver->Write(hkey, (const char *)buff, &writeCount); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Write failed (err %ld)\n", cfsStatus); + status = ARM_DRIVER_ERROR; + goto out; + } + if(writeCount != buffSize){ + CFSTORE_DBGLOG("Write failed, amount is %d while requested is %d\n", (int)writeCount, (int)buffSize); + status = ARM_DRIVER_ERROR; + goto out; + } + CFSTORE_DBGLOG("After write%s", "\n"); +out: + cfsStatus = cfstoreDriver->Close(hkey); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Close failed (err %ld)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + CFSTORE_DBGLOG("After close%s", "\n"); + /* Flash-to-flash only on success */ + if (status == ARM_DRIVER_OK) { + cfsStatus = cfstoreDriver->Flush(); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("Flush to flash failed(err %ld)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + CFSTORE_DBGLOG("After flush%s", "\n"); + } + CFSTORE_DBGLOG("%s:OUT: status=%d\n", __func__, (int) status); + return status; +} + + +int32_t cfstore_flush3_start(void) +{ + int32_t status = ARM_DRIVER_OK; + int32_t cfsStatus; + ARM_CFSTORE_DRIVER *cfstoreDriver = &cfstore_driver; + ARM_CFSTORE_CAPABILITIES caps; + + CFSTORE_DBGLOG("%s:IN\n", __func__); + + /* Initialize configuration store */ + cfsStatus = cfstoreDriver->Initialize(NULL, NULL); + if(cfsStatus < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("CFStore Initialization failed (err %lu)\n", cfsStatus); + return ARM_DRIVER_ERROR; + } + /* Get capabilities */ + memset(&caps, 0, sizeof(caps)); + caps = cfstoreDriver->GetCapabilities(); + if(caps.asynchronous_ops == true){ + CFSTORE_DBGLOG("%s:Please configure CFstore to work in synchronous mode. This can be change in config.json file.\n", __func__); + status = ARM_DRIVER_ERROR; + goto out; + } + if(caps.uvisor_support_enabled == true){ + CFSTORE_DBGLOG("%s:Please enable uvisor spv box.\n", __func__); + status = ARM_DRIVER_ERROR; + goto out; + } + CFSTORE_DBGLOG("%s:OUT: returning ARM_DRIVER_OK\n", __func__); + return ARM_DRIVER_OK; /* init succeeded */ + +out: + /* init failed */ + (void) cfstore_flush3_end(); + CFSTORE_DBGLOG("%s:OUT: status=%d\n", __func__, (int) status); + return status; +} + +int32_t cfstore_flush3_check_data(uint8_t* data, int32_t len, uint8_t val) +{ + int i; + for(i = 0; i < len; i++) { + if(data[i] != val){ + /* found byte which doesnt have the expected data value */ + return ARM_DRIVER_ERROR; + } + } + return ARM_DRIVER_OK; +} + +/* @brief test case to recreate the transactions documented in the + * issue 17 trace log. + * + * the test case was created by recreating the entries found in the + * log report grepped from the trace log attached to githug issue 17: + * 20160622_1321_grep_pv_configuration_store_issue_17_heap_corruption.txt + * + * This is what the test is intended to do: + * - A (setup) + * - cfstore_flush3_start + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 220) to create KV + * - cfstore_flush3_end + * - B (setup?) + * - cfstore_flush3_start + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.meta) + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.0) + * - cfstore_flush3_end + * - C (delete everything) + * - cfstore_flush3_start + * - cfstore_flush3_delete_file(com.arm.mbed.spv.sst.node.0). this will work as the KV is present + * - cfstore_flush3_delete_file(com.arm.mbed.spv.sst.node.x) where x = {1..29} + * - cfstore_flush3_delete_file(name com.arm.mbed.spv.sst.meta). + * - this should work as the KV is present + * - however, its is seen to fail in the trace log for the issue. + * - cfstore_flush3_end + * - D + * - cfstore_flush3_start + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.meta). readd 2280 bytes and check correct + * - this should fail as the kv has been deleted. + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 220) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV + * - cfstore_flush3_end + * - E + * - run C-D again, to delete everything + * - F + * - cfstore_flush3_start + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.meta). readd 2280 bytes and check correct + * - this should fail as the kv has been deleted. + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 220) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 816) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.1, len: 217) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 818) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV + * - cfstore_flush3_end + * - G + * - cfstore_flush3_start + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.meta) 2280 bytes should be read + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.0) 818 bytes should be read + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.1) 217 bytes should be read + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.1) 217 bytes should be read + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.1) 217 bytes should be read + * - cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.1) 217 bytes should be read + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.2, len: 235) to create KV + * - cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV + * - cfstore_flush3_end + * + * The issue reported heap corruption. To this end, a heap buffer + * is allocated and checked as a canary against heap corruption + */ + +#define CFSTORE_FLUSH3_TEST_DATA_CHAR 'A' /* 0b1010 bit pattern */ +#define CFSTORE_FLUSH3_HEAP_DATA_CHAR 'Z' +#define CFSTORE_FLUSH3_TEST_DATA_BUF_LEN 3000 /* 2280 was the largest buffer used*/ + +#ifdef TARGET_K64F +#define CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN 87500 /* alloc a heap buffer to use up some memory: value tuned to K64F */ +#else +#define CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN 1000 /* alloc a heap buffer to use up some memory (minimal for non-k64f targets until tuned */ +#endif /* TARGET_K64F */ + +static control_t cfstore_flush3_test_01(const size_t call_count) +{ + int32_t i; + int32_t ret = ARM_DRIVER_ERROR; + const char* kv_name_root = "com.arm.mbed.spv.sst"; + const char* kv_name_node= "node"; + const char* kv_name_meta= "meta"; + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint8_t data[CFSTORE_FLUSH3_TEST_DATA_BUF_LEN]; + void* heap_buf = NULL; + + (void) call_count; + heap_buf = malloc(CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: malloc() failed .\n", __func__); + TEST_ASSERT_MESSAGE(heap_buf != NULL, cfstore_flush3_utest_msg_g); + memset(heap_buf, CFSTORE_FLUSH3_HEAP_DATA_CHAR, CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN); + + CFSTORE_DBGLOG("%s: - A (setup)\n", __func__); + CFSTORE_DBGLOG("%s: - cfstore_flush3_start\n", __func__); + ret = cfstore_flush3_start(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: A.1 cfstore_flush3_start() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV\n", __func__); + memset(data, CFSTORE_FLUSH3_TEST_DATA_CHAR, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, kv_name_meta); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: A.2 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 220) to create KV\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 220); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: A.3 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - cfstore_flush3_end\n", __func__); + ret = cfstore_flush3_end(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: A.4 cfstore_flush3_end() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: check for heap corruption\n", __func__); + ret = cfstore_flush3_check_data((uint8_t*) heap_buf, CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN, CFSTORE_FLUSH3_HEAP_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: A.5 cfstore_flush3_check_data() failed for heap (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - B\n", __func__); + CFSTORE_DBGLOG("%s: - cfstore_flush3_start\n", __func__); + ret = cfstore_flush3_start(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: B.1 cfstore_flush3_start() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_read_file(com.arm.mbed.spv.sst.meta). read 2280\n", __func__); + memset(data, 0, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_read_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: B.2 cfstore_flush3_read_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + ret = cfstore_flush3_check_data(data, 2280, CFSTORE_FLUSH3_TEST_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: B.3 cfstore_flush3_check_data() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.0). read 220\n", __func__); + memset(data, 0, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_read_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, data, 220); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: B.4 cfstore_flush3_read_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + ret = cfstore_flush3_check_data(data, 220, CFSTORE_FLUSH3_TEST_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: B.5 cfstore_flush3_check_data() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - cfstore_flush3_end\n", __func__); + ret = cfstore_flush3_end(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: B.6 cfstore_flush3_end() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: check for heap corruption\n", __func__); + ret = cfstore_flush3_check_data((uint8_t*) heap_buf, CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN, CFSTORE_FLUSH3_HEAP_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: B.7 cfstore_flush3_check_data() failed for heap (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - C (delete everything)\n", __func__); + CFSTORE_DBGLOG("%s: - cfstore_flush3_start\n", __func__); + ret = cfstore_flush3_start(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: C.1 cfstore_flush3_start() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_delete_file(com.arm.mbed.spv.sst.node.0). this will work as the KV is present\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_delete_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: C.2 cfstore_flush3_delete_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_delete_file(com.arm.mbed.spv.sst.node.x) where x = {1..29}, each of which should fail\n", __func__); + for(i = 1; i <= 29; i++){ + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.%d", (char*) kv_name_root, (char*) kv_name_node, (int) i); + ret = cfstore_flush3_delete_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: C.%d cfstore_flush3_delete_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) i+2, (int) ret, kv_name); + /* The delete operations are expected to fail as the keys dont exist + * but cfstore_flush3_delete_file() returns ARM_DRIVER_OK when key isnt found, so cant test the return code. + */ + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + } + + CFSTORE_DBGLOG("%s: cfstore_flush3_delete_file(name com.arm.mbed.spv.sst.meta). this is expected to succeed as the KV is present\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_delete_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: C.32 cfstore_flush3_delete_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + +#ifdef CFSTORE_DEBUG + CFSTORE_DBGLOG("%s: cfstore_test_dump: dump here contents of CFSTORE so we know whats present\n", __func__); + ret = cfstore_test_dump(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.1.1 cfstore_test_dump failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); +#endif /* CFSTORE_DEBUG */ + + CFSTORE_DBGLOG("%s: - cfstore_flush3_end\n", __func__); + ret = cfstore_flush3_end(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: C.33 cfstore_flush3_end() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: check for heap corruption\n", __func__); + ret = cfstore_flush3_check_data((uint8_t*) heap_buf, CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN, CFSTORE_FLUSH3_HEAP_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: C.34 cfstore_flush3_check_data() failed for heap (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - D\n", __func__); + CFSTORE_DBGLOG("%s: - cfstore_flush3_start\n", __func__); + ret = cfstore_flush3_start(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.1 cfstore_flush3_start() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + +#ifdef CFSTORE_DEBUG + CFSTORE_DBGLOG("%s: cfstore_test_dump: dump here contents of CFSTORE so we know whats present\n", __func__); + ret = cfstore_test_dump(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.1.1 cfstore_test_dump failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); +#endif /* CFSTORE_DEBUG */ + + CFSTORE_DBGLOG("%s: cfstore_flush3_read_file(com.arm.mbed.spv.sst.meta). this should fail as the kv has been deleted.\n", __func__); + memset(data, 0, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_read_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.2 cfstore_flush3_read_file() succeeded when expected to fail (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 220) to create KV.\n", __func__); + memset(data, CFSTORE_FLUSH3_TEST_DATA_CHAR, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 220); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.3 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV.\n", __func__); + memset(data, CFSTORE_FLUSH3_TEST_DATA_CHAR, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.4 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - cfstore_flush3_end\n", __func__); + ret = cfstore_flush3_end(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.5 cfstore_flush3_end() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: check for heap corruption\n", __func__); + ret = cfstore_flush3_check_data((uint8_t*) heap_buf, CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN, CFSTORE_FLUSH3_HEAP_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: D.6 cfstore_flush3_check_data() failed for heap (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - E: run C-D again, to delete everything\n", __func__); + CFSTORE_DBGLOG("%s: - cfstore_flush3_start\n", __func__); + ret = cfstore_flush3_start(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: E.1 cfstore_flush3_start() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_delete_file(com.arm.mbed.spv.sst.node.0). this will work as the KV is present.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_delete_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: E.2 cfstore_flush3_delete_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_delete_file(com.arm.mbed.spv.sst.node.x) where x = {1..29}, each of which should fail.\n", __func__); + for(i = 1; i <= 29; i++){ + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.%d", (char*) kv_name_root, (char*) kv_name_node, (int) i); + ret = cfstore_flush3_delete_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: E.%d cfstore_flush3_delete_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) i+2, (int) ret, kv_name); + /* The delete operations are expected to fail as the keys dont exist + * but cfstore_flush3_delete_file() returns ARM_DRIVER_OK when key isnt found, so cant test the return code. + */ + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + } + + CFSTORE_DBGLOG("%s: cfstore_flush3_delete_file(name com.arm.mbed.spv.sst.meta). this is expected to succeed as the KV is present.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_delete_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: E.32 cfstore_flush3_delete_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - cfstore_flush3_end\n", __func__); + ret = cfstore_flush3_end(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: E.33 cfstore_flush3_end() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: check for heap corruption\n", __func__); + ret = cfstore_flush3_check_data((uint8_t*) heap_buf, CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN, CFSTORE_FLUSH3_HEAP_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: E.34 cfstore_flush3_check_data() failed for heap (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - F\n", __func__); + CFSTORE_DBGLOG("%s: - cfstore_flush3_start\n", __func__); + ret = cfstore_flush3_start(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.1 cfstore_flush3_start() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_read_file(com.arm.mbed.spv.sst.meta). this should fail as the kv has been deleted.\n", __func__); + memset(data, 0, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_read_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.2 cfstore_flush3_read_file() succeeded when expected to fail (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + memset(data, CFSTORE_FLUSH3_TEST_DATA_CHAR, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + /* cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 220) to create KV */ + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 220); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.3 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.4 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 816) to create KV.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 816); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.5 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.6 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.1, len: 217) to create KV.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.1", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 217); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.7 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.8 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.0, len: 818) to create KV.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 818); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.9 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.10 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - cfstore_flush3_end\n", __func__); + ret = cfstore_flush3_end(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.11 cfstore_flush3_end() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: check for heap corruption\n", __func__); + ret = cfstore_flush3_check_data((uint8_t*) heap_buf, CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN, CFSTORE_FLUSH3_HEAP_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.12 cfstore_flush3_check_data() failed for heap (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - G\n", __func__); + CFSTORE_DBGLOG("%s: - cfstore_flush3_start\n", __func__); + ret = cfstore_flush3_start(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: G.1 cfstore_flush3_start() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_read_file(com.arm.mbed.spv.sst.meta) 2280 bytes should be read.\n", __func__); + memset(data, 0, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_read_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: G.2 cfstore_flush3_read_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + ret = cfstore_flush3_check_data(data, 2280, CFSTORE_FLUSH3_TEST_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: G.3 cfstore_flush3_check_data() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.0) 818 bytes should be read.\n", __func__); + memset(data, 0, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_read_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, data, 818); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: G.4 cfstore_flush3_read_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + ret = cfstore_flush3_check_data(data, 818, CFSTORE_FLUSH3_TEST_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: G.5 cfstore_flush3_check_data() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_read_file(com.arm.mbed.spv.sst.node.1) 217 bytes should be read. repeat 4 times.\n", __func__); + for(i = 0; i < 4; i++){ + memset(data, 0, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.1", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_read_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, data, 217); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: G.%d.1 cfstore_flush3_read_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) i+6, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + ret = cfstore_flush3_check_data(data, 217, CFSTORE_FLUSH3_TEST_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: G.%d.2 cfstore_flush3_check_data() failed (ret=%d, kv_name=%s).\n", __func__, (int) i+6, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + } + + memset(data, CFSTORE_FLUSH3_TEST_DATA_CHAR, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.node.2, len: 235) to create KV.\n", __func__); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s.0", (char*) kv_name_root, (char*) kv_name_node); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 235); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.3 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: cfstore_flush3_write_file(com.arm.mbed.spv.sst.meta, len: 2280) to create KV.\n", __func__); + memset(data, CFSTORE_FLUSH3_TEST_DATA_CHAR, CFSTORE_FLUSH3_TEST_DATA_BUF_LEN); + snprintf(kv_name, CFSTORE_KEY_NAME_MAX_LENGTH, "%s.%s", (char*) kv_name_root, (char*) kv_name_meta); + ret = cfstore_flush3_write_file(NULL, CFSTORE_KEY_NAME_MAX_LENGTH, (const char*) kv_name, (const uint8_t*) data, 2280); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: A.2 cfstore_flush3_write_file() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: - cfstore_flush3_end\n", __func__); + ret = cfstore_flush3_end(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: F.11 cfstore_flush3_end() failed (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s: check for heap corruption\n", __func__); + ret = cfstore_flush3_check_data((uint8_t*) heap_buf, CFSTORE_FLUSH3_TEST_HEAP_BUF_LEN, CFSTORE_FLUSH3_HEAP_DATA_CHAR); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: A.5 cfstore_flush3_check_data() failed for heap (ret=%d, kv_name=%s).\n", __func__, (int) ret, kv_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + if(heap_buf){ + free(heap_buf); + } + + return CaseNext; +} + + +/* @brief simple flush test */ +static control_t cfstore_flush3_test_02(const size_t call_count) +{ + int32_t cfsStatus = ARM_DRIVER_ERROR; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_SIZE len = strlen("key0"); + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + CFSTORE_DBGLOG("%s:Initialize()\n", __func__); + cfsStatus = drv->Initialize(NULL, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s:Create()\n", __func__); + cfsStatus = drv->Create("key0", len, &kdesc, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + len = strlen("some-value"); + CFSTORE_DBGLOG("%s:Write()\n", __func__); + cfsStatus = drv->Write(hkey, "some-value", &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s:Close()\n", __func__); + cfsStatus = drv->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s:Flush()\n", __func__); + cfsStatus = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s:Open()\n", __func__); + cfsStatus = drv->Open("key0", flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s:Delete()\n", __func__); + cfsStatus = drv->Delete(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s:Close()\n", __func__); + cfsStatus = drv->Close(hkey); /////// <--- cfsStatus = ARM_CFSTORE_DRIVER_ERROR_PREEXISTING_KEY + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + CFSTORE_DBGLOG("%s:got status = %d\n", __func__, (int) cfsStatus); + + CFSTORE_DBGLOG("%s:Uninitialize()\n", __func__); + cfsStatus = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error:%d:cfsStatus=%d", __func__, __LINE__, (int) cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + + return CaseNext; +} + +#endif /* STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS && STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS==0 */ + + +static control_t cfstore_flush3_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_CAPABILITIES caps;; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + (void) call_count; + + /* initialise the context */ + caps = drv->GetCapabilities(); + CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, caps.asynchronous_ops); + if(caps.asynchronous_ops == true){ + /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true + * This means the test will conveniently pass when run in CI as part of async mode testing */ + CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); + return CaseNext; + } + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush3_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush3_utest_msg_g); + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(CFSTORE_FLUSH3_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FLUSH3_test_00", cfstore_flush3_test_00), +#if defined STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS && STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS==0 + Case("FLUSH3_test_01", cfstore_flush3_test_01), + Case("FLUSH3_test_02", cfstore_flush3_test_02), +#endif // STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +/// @cond + diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/init/init.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/init/init.cpp new file mode 100644 index 0000000000..2f6d3b569c --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/init/init.cpp @@ -0,0 +1,156 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file init.cpp Test cases to test CFSTORE initialization/uninitialization code. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "Driver_Common.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + +#include +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_init_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/// @cond CFSTORE_DOXYGEN_DISABLE +typedef struct cfstore_init_ctx_t +{ + ARM_CFSTORE_CAPABILITIES caps; +} cfstore_init_ctx_t; + +static cfstore_init_ctx_t cfstore_init_ctx_g; +extern ARM_CFSTORE_DRIVER cfstore_driver; +ARM_CFSTORE_DRIVER *cfstore_drv = &cfstore_driver; +/// @endcond + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_init_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_init_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_init_utest_msg_g); + return CaseNext; +} + +static void cfstore_init_test_01(cfstore_init_ctx_t* ctx) +{ + int32_t ret; + + (void) ctx; + CFSTORE_DBGLOG("INITIALIZING%s", "\r\n"); + ret = cfstore_drv->Initialize(NULL, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_init_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_init_utest_msg_g); + + CFSTORE_DBGLOG("FLUSHING1%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_init_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_init_utest_msg_g); + + CFSTORE_DBGLOG("UNINITIALIZING%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_init_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() should return ret >= 0 for synch mode(ret=%ld)\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_init_utest_msg_g); + + CFSTORE_DBGLOG("***************%s", "\r\n"); + CFSTORE_DBGLOG("*** SUCCESS ***%s", "\r\n"); + CFSTORE_DBGLOG("***************%s", "\r\n"); + return; +} + +static control_t cfstore_init_app_start(const size_t call_count) +{ + cfstore_init_ctx_t* ctx = &cfstore_init_ctx_g; + + (void) call_count; + + /* initialise the context */ + memset(ctx, 0, sizeof(cfstore_init_ctx_t)); + ctx->caps = cfstore_drv->GetCapabilities(); + CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops); + if(ctx->caps.asynchronous_ops == true){ + /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true + * This means the test will conveniently pass when run in CI as part of async mode testing */ + CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); + return CaseNext; + } + cfstore_init_test_01(ctx); + return CaseNext; +} + +#ifndef YOTTA_CONFIGURATION_STORE_INIT_VERSION_STRING + + +/* when built as Configuration-Store example, include greentea support otherwise omit */ + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("INIT_test_00", cfstore_init_test_00), + Case("INIT_test_01_start", cfstore_init_app_start), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond + + +#else // YOTTA_CONFIGURATION_STORE_INIT_VERSION_STRING + + +// stand alone Configuration-Store-Example +void app_start(int argc __unused, char** argv __unused) +{ + cfstore_init_app_start(0); +} + + +#endif // YOTTA_CONFIGURATION_STORE_INIT_VERSION_STRING diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/misc/misc.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/misc/misc.cpp new file mode 100644 index 0000000000..20d46533ce --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/misc/misc.cpp @@ -0,0 +1,340 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file misc.cpp Test cases for miscellaneous API drv->Xxx() functions. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "cfstore_config.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_misc_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +/* Create the main box ACL list for the application. + * The main ACL gets inherited by all the other boxes + */ +CFSTORE_UVISOR_MAIN_ACL(cfstore_acl_uvisor_box_misc_g); + +/* Enable uVisor. */ +UVISOR_SET_MODE_ACL(UVISOR_ENABLED, cfstore_acl_uvisor_box_misc_g); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_misc_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/** @brief basic PowerControl() test + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_misc_test_00_start(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_POWER_STATE state = ARM_POWER_OFF; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + /* try setting the power control state before initialised, which should fail */ + ret = drv->PowerControl(state); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Eror: PowerControl() call should have failed as CFSTORE not initialised, but the call succeeded\r\n", __func__); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + ret = drv->Initialize(cfstore_utest_default_callback, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseTimeout(CFSTORE_UTEST_DEFAULT_TIMEOUT_MS); +} + +static control_t cfstore_misc_test_00_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_POWER_STATE state = ARM_POWER_OFF; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + while(state <= ARM_POWER_FULL) + { + ret = drv->PowerControl(state); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: PowerControl() call failed\r\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + state = (ARM_POWER_STATE)((uint32_t) state + 1); + } + /*try invalid value which should fail*/ + ret = drv->PowerControl((ARM_POWER_STATE ) 0xffffffff); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:ERror: PowerControl() did not fail with bad value 0xffffffff (not as expected)\r\n", __func__); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/** @brief basic GetVersion() test + * + */ +control_t cfstore_misc_test_01(const size_t call_count) +{ + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_DRIVER_VERSION version; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&version, 0, sizeof(version)); + + version = drv->GetVersion(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:GetVersion() failed to return correct API version.\r\n", __func__); + TEST_ASSERT_MESSAGE(version.api == ARM_CFSTORE_API_VERSION, cfstore_misc_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:GetVersion() to return correct driver version.\r\n", __func__); + TEST_ASSERT_MESSAGE(version.drv == ARM_CFSTORE_DRV_VERSION, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/* KV data for test_03 */ +static cfstore_kv_data_t cfstore_misc_test_03_kv_data[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + { "The.principles.of.least.action.in.quantum.mechanics", "DoctoralThesis"}, + { "Space.Time.Approach.to.Quantum.Electrodynamic", " PhysicalReview766)"}, + { "An.Operator.Calculus.Having.Applications.in.Quantum.Electrodynamics", "PhysicalReview84)"}, + { NULL, NULL}, +}; + + +/** @brief basic GetKeyName() test + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_misc_test_02_end(const size_t call_count) +{ + uint8_t key_name_len = 0; + char key_name_buf[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + (void) call_count; + memset(key_name_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + + ret = cfstore_test_create_table(cfstore_misc_test_03_kv_data); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to create keys from table.\r\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + node = cfstore_misc_test_03_kv_data; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:About to open KV (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + ret = drv->Open(node->key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to open node (key_name=\"%s\", value=\"%s\")(ret=%d)\r\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + key_name_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + memset(key_name_buf, 0, key_name_len); + drv->GetKeyName(hkey, key_name_buf, &key_name_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to GetKeyName() (key_name (expected)=\"%s\", key_name (returned)=\"%s\", value=\"%s\"), len return=%d, len expected=%d\r\n", __func__, node->key_name, key_name_buf, node->value, (int) key_name_len, (int) strlen(node->key_name)); + TEST_ASSERT_MESSAGE(key_name_len == strlen(node->key_name)+1, cfstore_misc_utest_msg_g); + + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("GetKeyName() successfully reported key_name (key_name=\"%s\")\r\n", key_name_buf); + ret = drv->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to close key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + node++; + } + + cfstore_test_delete_all(); + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/** @brief basic GetValueLen() test + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_misc_test_03_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + + ret = cfstore_test_create_table(cfstore_misc_test_03_kv_data); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to create keys from table.\r\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + node = cfstore_misc_test_03_kv_data; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:About to open KV (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + ret = drv->Open(node->key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to open node (key_name=\"%s\", value=\"%s\")(ret=%d)\r\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + drv->GetValueLen(hkey, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Errro: GetValueLen() retrieve incorrect length of value blob(expected=%d, returned=%d)\r\n", __func__, (int) strlen(node->value), (int) len); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_misc_utest_msg_g); + /* revert to CFSTORE_LOG if more trace required */ + CFSTORE_DBGLOG("GetValueLen() successfully reported correct value blob length%s", "\r\n"); + + ret = drv->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to close key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + node++; + } + cfstore_test_delete_all(); + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/** @brief basic GetStatus() test + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_misc_test_04_start(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_STATUS status; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + status = drv->GetStatus(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() before initialisation should have reported error, but reported no error.\r\n", __func__); + TEST_ASSERT_MESSAGE(status.error == true, cfstore_misc_utest_msg_g); + + ret = drv->Initialize(cfstore_utest_default_callback, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseTimeout(CFSTORE_UTEST_DEFAULT_TIMEOUT_MS); +} + +/** @brief basic GetStatus() test + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_misc_test_04_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_STATUS status; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + status = drv->GetStatus(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() but reported error.\r\n", __func__); + TEST_ASSERT_MESSAGE(status.error == false, cfstore_misc_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() reported operation in progress.\r\n", __func__); + TEST_ASSERT_MESSAGE(status.in_progress == false, cfstore_misc_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(200, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("MISC_test_00", cfstore_misc_test_00), + Case("MISC_test_00_start", cfstore_misc_test_00_start), + Case("MISC_test_00_end", cfstore_misc_test_00_end), + Case("MISC_test_01", cfstore_misc_test_01), + Case("MISC_test_02_start", cfstore_utest_default_start), + Case("MISC_test_02_end", cfstore_misc_test_02_end), + Case("MISC_test_03_start", cfstore_utest_default_start), + Case("MISC_test_03_end", cfstore_misc_test_03_end), + Case("MISC_test_04_start", cfstore_misc_test_04_start), + Case("MISC_test_05_end", cfstore_misc_test_04_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/open/open.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/open/open.cpp new file mode 100644 index 0000000000..d1158f4ff3 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/open/open.cpp @@ -0,0 +1,641 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file open.cpp Test cases to open KVs in the CFSTORE using the drv->Open() interface. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include /*rand()*/ +#include + +using namespace utest::v1; + +static char cfstore_open_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.open.box1"); +UVISOR_BOX_CONFIG(cfstore_open_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/// @cond CFSTORE_DOXYGEN_DISABLE +#ifdef CFSTORE_DEBUG +#define CFSTORE_OPEN_GREENTEA_TIMEOUT_S 3000 +#else +#define CFSTORE_OPEN_GREENTEA_TIMEOUT_S 1000 +#endif +/// @endcond + + +/* support functions */ + +/* + * open tests that focus on testing cfstore_open() + * cfstore_handle_t cfstore_open(const char* key_name, char* data, ARM_CFSTORE_SIZE* len, cfstore_key_desc_t* kdesc) + */ + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_open_test_01_kv_data[] = { + { "yotta.hello-world.animal{wobbly-dog}{foot}frontLeft", "missing"}, + { NULL, NULL}, +}; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_open_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + +/** @brief + * Basic open test which does the following: + * - creates KV with default rw perms and writes some data to the value blob. + * - closes the newly created KV. + * - opens the KV with the default permissions (r-only) + * - reads the KV data and checks its the same as the previously created data. + * - closes the opened key + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_open_test_01_end(const size_t call_count) +{ + char* read_buf; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + cfstore_kv_data_t *node; + ARM_CFSTORE_FMODE flags; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + node = cfstore_open_test_01_kv_data; + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + CFSTORE_DBGLOG("%s:About to create new node (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + ret = drv->Create(node->key_name, strlen(node->value), &kdesc, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create node (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_DBGLOG("%s:length of KV=%d (key_name=\"%s\", value=\"%s\")\n", __func__, (int) len, node->key_name, node->value); + len = strlen(node->value); + ret = drv->Write(hkey, (char*) node->value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d, (ret=%d)\n", __func__, node->key_name, node->value, (int) len, (int) ret); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_open_utest_msg_g); + + CFSTORE_DBGLOG("Created KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + ret = drv->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close handle (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + /* now open the newly created key */ + ret = drv->Open(node->key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + len = strlen(node->value) + 1; + read_buf = (char*) malloc(len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to allocated read buffer \n", __func__); + TEST_ASSERT_MESSAGE(read_buf != NULL, cfstore_open_utest_msg_g); + + CFSTORE_DBGLOG("Opened KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + memset(read_buf, 0, len); + ret = drv->Read(hkey, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + /* check read data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\n", __func__, read_buf, node->key_name, node->value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, cfstore_open_utest_msg_g); + + if(read_buf){ + free(read_buf); + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + +static cfstore_kv_data_t cfstore_open_test_02_data[] = { + CFSTORE_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + +/** + * @brief test to open() a pre-existing key and try to write it, which should fail + * as by default pre-existing keys are opened read-only + * + * Basic open test which does the following: + * - creates KV with default rw perms and writes some data to the value blob. + * - closes the newly created KV. + * - opens the KV with the default permissions (read-only) + * - tries to write the KV data which should fail because KV was not opened with write flag set. + * - closes the opened key + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_open_test_02_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_open_test_02_data[0].value); + ret = cfstore_test_create(cfstore_open_test_02_data[0].key_name, (char*) cfstore_open_test_02_data[0].value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + /* by default, owner of key opens with read-only permissions*/ + ret = drv->Open(cfstore_open_test_02_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\")(ret=%d)\n", __func__, cfstore_open_test_02_data[0].key_name, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + len = strlen(cfstore_open_test_02_data[0].value); + ret = drv->Write(hkey, cfstore_open_test_02_data[0].value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: call to Write() succeeded when should have failed (key_name=\"%s\")(ret=%d).\n", __func__, cfstore_open_test_02_data[0].key_name, (int) ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_READ_ONLY, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + + +/** + * @brief test to open() a pre-existing key and try to write it, which should succeed + * because the key was opened read-write permissions explicitly + * + * Basic open test which does the following: + * - creates KV with default rw perms and writes some data to the value blob. + * - closes the newly created KV. + * - opens the KV with the rw permissions (non default) + * - tries to write the KV data which should succeeds because KV was opened with write flag set. + * - closes the opened key + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_open_test_03_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_open_test_02_data[0].value); + ret = cfstore_test_create(cfstore_open_test_02_data[0].key_name, (char*) cfstore_open_test_02_data[0].value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + /* opens with read-write permissions*/ + flags.read = true; + flags.write = true; + ret = drv->Open(cfstore_open_test_02_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\")(ret=%d)\n", __func__, cfstore_open_test_02_data[0].key_name, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + len = strlen(cfstore_open_test_02_data[0].value); + ret = drv->Write(hkey, cfstore_open_test_02_data[0].value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: call to Write() failed when should have succeeded (key_name=\"%s\")(ret=%d).\n", __func__, cfstore_open_test_02_data[0].key_name, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + + + +/** @brief test to call cfstore_open() with a key_name string that exceeds + * the maximum length + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_open_test_04_end(const size_t call_count) +{ + char kv_name_good[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + char kv_name_bad[CFSTORE_KEY_NAME_MAX_LENGTH+2]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(kv_name_good, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(kv_name_bad, 0, CFSTORE_KEY_NAME_MAX_LENGTH+2); + memset(&kdesc, 0, sizeof(kdesc)); + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + + len = CFSTORE_KEY_NAME_MAX_LENGTH; + ret = cfstore_test_kv_name_gen(kv_name_good, len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name_good.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: kv_name_good is not the correct length (len=%d, expected=%d).\n", __func__, (int) strlen(kv_name_good), (int) len); + TEST_ASSERT_MESSAGE(strlen(kv_name_good) == CFSTORE_KEY_NAME_MAX_LENGTH, cfstore_open_utest_msg_g); + + ret = cfstore_test_create(kv_name_good, kv_name_good, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store for kv_name_good(ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = cfstore_test_kv_name_gen(kv_name_bad, len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name_bad.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: kv_name_bad is not the correct length (len=%d, expected=%d).\n", __func__, (int) strlen(kv_name_bad), (int) len); + TEST_ASSERT_MESSAGE(strlen(kv_name_bad) == CFSTORE_KEY_NAME_MAX_LENGTH+1, cfstore_open_utest_msg_g); + + memset(&kdesc, 0, sizeof(kdesc)); + ret = cfstore_test_create(kv_name_bad, kv_name_bad, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: created KV in store for kv_name_bad when should have failed(ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +typedef struct cfstore_open_kv_name_ascii_node { + uint32_t code; + uint32_t f_allowed : 1; +} cfstore_open_kv_name_ascii_node; +/// @endcond + +static const uint32_t cfstore_open_kv_name_ascii_table_code_sentinel_g = 256; + +/*@brief table recording ascii character codes permitted in kv names */ +static cfstore_open_kv_name_ascii_node cfstore_open_kv_name_ascii_table[] = +{ + {0, false}, /* codes 0-44 not allowed */ + {45, true}, /* codes 45-46 == [-.] allowed */ + {47, false}, /* code 47 not allowed */ + {48, true}, /* codes 48-57 not allowed */ + {58, false}, /* codes 46-64 not allowed */ + {64, true}, /* codes 64-91 allowed [@A-Z] */ + {91, false}, /* code 91-96 not allowed */ + {95, true}, /* code 95 allowed '_' */ + {96, false}, /* codes 96 not allowed */ + {97, true}, /* codes 65-90 allowed [A-Z] and {*/ + {123, false}, /* codes 123 '}' not allowed on its own*/ + {124, false}, /* codes 124 not allowed */ + {125, false}, /* code 125 '}' not allowed on its own*/ + {126, false}, /* codes 126-255 not allowed */ + {cfstore_open_kv_name_ascii_table_code_sentinel_g, false}, /* sentinel */ +}; + +/// @cond CFSTORE_DOXYGEN_DISABLE +enum cfstore_open_kv_name_pos { + cfstore_open_kv_name_pos_start = 0x0, + cfstore_open_kv_name_pos_mid, + cfstore_open_kv_name_pos_end, + cfstore_open_kv_name_pos_max +}; +/// @endcond + +/** @brief test to call cfstore_open() with key_name that in includes + * illegal characters + * - the character(s) can be at the beginning of the key_name + * - the character(s) can be at the end of the key_name + * - the character(s) can be somewhere within the key_name string + * - a max-length string of random characters (legal and illegal) + * - a max-length string of random illegal characters only + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_open_test_05_end(const size_t call_count) +{ + bool f_allowed = false; + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + uint32_t j = 0; + int32_t ret = ARM_DRIVER_OK; + size_t name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + ARM_CFSTORE_KEYDESC kdesc; + cfstore_open_kv_name_ascii_node* node = NULL; + uint32_t pos; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + +#ifdef CFSTORE_DEBUG + /* symbol only used why debug is enabled */ + const char* pos_str = NULL; +#endif + + /* create bad keyname strings with invalid character code at start of keyname */ + node = cfstore_open_kv_name_ascii_table; + while(node->code != cfstore_open_kv_name_ascii_table_code_sentinel_g) + { + /* loop over range */ + for(j = node->code; j < (node+1)->code; j++) + { + /* set the start, mid, last character of the name to the test char code */ + for(pos = (uint32_t) cfstore_open_kv_name_pos_start; pos < (uint32_t) cfstore_open_kv_name_pos_max; pos++) + { + name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(kv_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(&kdesc, 0, sizeof(kdesc)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + + ret = cfstore_test_kv_name_gen(kv_name, name_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_open_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: kv_name incorrect length (len=%d, expected= %d).\n", __func__, (int) strlen(kv_name), (int) name_len); + TEST_ASSERT_MESSAGE(strlen(kv_name) == name_len, cfstore_open_utest_msg_g); + + /* overwrite a char at the pos start, mid, end of the kv_name with an ascii char code (both illegal and legal)*/ + switch(pos) + { + case cfstore_open_kv_name_pos_start: + kv_name[0] = (char) j; + break; + case cfstore_open_kv_name_pos_mid: + /* create bad keyname strings with invalid character code in the middle of keyname */ + kv_name[name_len/2] = (char) j; + break; + case cfstore_open_kv_name_pos_end: + /* create bad keyname strings with invalid character code at end of keyname */ + kv_name[name_len-1] = (char) j; + break; + default: + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected value of pos (pos=%d).\n", __func__, (int) pos); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + break; + } + +#ifdef CFSTORE_DEBUG + /* processing only required when debug trace enabled */ + switch(pos) + { + case cfstore_open_kv_name_pos_start: + pos_str = "start"; + break; + case cfstore_open_kv_name_pos_mid: + pos_str = "middle"; + break; + case cfstore_open_kv_name_pos_end: + pos_str = "end"; + break; + default: + break; + } +#endif + ret = cfstore_test_create(kv_name, kv_name, &name_len, &kdesc); + + /* special cases */ + switch(j) + { + case 0 : + case 46 : + switch(pos) + { + /* for code = 0 (null terminator). permitted at mid and end of string */ + /* for code = 46 ('.'). permitted at mid and end of string but not at start */ + case cfstore_open_kv_name_pos_start: + f_allowed = false; + break; + case cfstore_open_kv_name_pos_mid: + case cfstore_open_kv_name_pos_end: + default: + f_allowed = true; + break; + } + break; + default: + f_allowed = node->f_allowed; + break; + } + if(f_allowed == true) + { + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store when kv_name contains valid characters (code=%d, ret=%d).\n", __func__, (int) j, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("Successfully created a KV with valid keyname containing ascii character code %d (%c) at the %s of the keyname.\n", (int) j, (int) j, pos_str); + CFSTORE_LOG("%c", '.'); + + ret = cfstore_test_delete(kv_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete KV previously created (code=%d, ret=%d).\n", __func__, (int) j, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + } + else + { /*node->f_allowed == false => not allowed to create kv name with ascii code */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: created KV in store when kv_name contains an invalid character (code=%d, ret=%d).\n", __func__, (int) j, (int) ret); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_open_utest_msg_g); + /* revert CFSTORE_LOG for more trace */ + CFSTORE_DBGLOG("Successfully failed to create a KV with an invalid keyname containing ascii character code %d at the %s of the keyname.\n", (int) j, pos_str); + CFSTORE_LOG("%c", '.'); + } + } + } + node++; + } + + CFSTORE_LOG("%c", '\n'); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + + +static const char cfstore_open_ascii_illegal_buf_g[] = "!\"�$%&'()*+,./:;<=>?@[\\]^_`{|}~"; /* 31 chars */ + +/** @brief test to call cfstore_open() with key_name that in includes + * illegal characters + * - a max-length string of random illegal characters only + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_open_test_06_end(const size_t call_count) +{ + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + size_t i = 0; + uint32_t pos = 0; + int32_t ret = ARM_DRIVER_OK; + size_t name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + ARM_CFSTORE_KEYDESC kdesc; + size_t buf_data_max = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + /* create bad keyname strings with invalid character code at start of keyname */ + buf_data_max = strlen(cfstore_open_ascii_illegal_buf_g); + name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(kv_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + + /* generate a kv name of illegal chars*/ + for(i = 0; i < name_len; i++) + { + pos = rand() % (buf_data_max+1); + kv_name[i] = cfstore_open_ascii_illegal_buf_g[pos]; + } + + ret = cfstore_test_create(kv_name, kv_name, &name_len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: created KV in store when kv_name contains invalid characters (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + +/** @brief test to call cfstore_open() with key_name that in includes + * illegal characters + * - a max-length string of random characters (legal and illegal) + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_open_test_07_end(const size_t call_count) +{ + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + size_t i = 0; + int32_t ret = ARM_DRIVER_OK; + size_t name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + ARM_CFSTORE_KEYDESC kdesc; + size_t buf_data_max = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + /* create bad keyname strings with invalid character code at start of keyname */ + buf_data_max = strlen(cfstore_open_ascii_illegal_buf_g); + name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(kv_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + + ret = cfstore_test_kv_name_gen(kv_name, name_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_open_utest_msg_g); + + /* pepper the illegal chars across the string*/ + for(i++; i < buf_data_max; i++){ + kv_name[rand() % (name_len+1)] = cfstore_open_ascii_illegal_buf_g[i]; + } + ret = cfstore_test_create(kv_name, kv_name, &name_len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: created KV in store when kv_name contains invalid characters (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(CFSTORE_OPEN_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("OPEN_test_00", cfstore_open_test_00), + Case("OPEN_test_01_start", cfstore_utest_default_start), + Case("OPEN_test_01_end", cfstore_open_test_01_end), + Case("OPEN_test_02_start", cfstore_utest_default_start), + Case("OPEN_test_02_end", cfstore_open_test_02_end), + Case("OPEN_test_03_start", cfstore_utest_default_start), + Case("OPEN_test_03_end", cfstore_open_test_03_end), + Case("OPEN_test_04_start", cfstore_utest_default_start), + Case("OPEN_test_04_end", cfstore_open_test_04_end), + Case("OPEN_test_05_start", cfstore_utest_default_start), + Case("OPEN_test_05_end", cfstore_open_test_05_end), + Case("OPEN_test_06_start", cfstore_utest_default_start), + Case("OPEN_test_06_end", cfstore_open_test_06_end), + Case("OPEN_test_07_start", cfstore_utest_default_start), + Case("OPEN_test_07_end", cfstore_open_test_07_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/read/read.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/read/read.cpp new file mode 100644 index 0000000000..53f03af718 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/read/read.cpp @@ -0,0 +1,188 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file read.cpp Test cases to read KVs in the CFSTORE using the drv->Read() API call. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_read_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.read.box1"); +UVISOR_BOX_CONFIG(cfstore_read_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_read_test_01_kv_data[] = { + CFSTORE_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_read_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + return CaseNext; +} + + +/* @brief check char at offset is as expected */ +static int32_t cfstore_read_seek_test(ARM_CFSTORE_HANDLE hkey, uint32_t offset, const char expected) +{ + ARM_CFSTORE_SIZE len = 1; + char read_buf[1]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + ret = drv->Rseek(hkey, offset); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:failed to Rseek() to desired offset (offset=%d) (ret=%d).\n", __func__, (int) offset, (int) ret); + goto out0; + } + ret = drv->Read(hkey, read_buf, &len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:failed to Read() (offset=%d)(ret=%d).\n", __func__, (int) offset, (int) ret); + goto out0; + } + if(read_buf[0] != expected){ + ret = ARM_DRIVER_ERROR; + goto out0; + } +out0: + return ret; +} + +/** @brief + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_read_test_01_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + cfstore_test_rw_data_entry_t *node; + ARM_CFSTORE_FMODE flags; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + /* create a key for reading */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_read_test_01_kv_data[0].value); + ret = cfstore_test_create(cfstore_read_test_01_kv_data[0].key_name, (char*) cfstore_read_test_01_kv_data[0].value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + + /* now open the newly created key */ + ret = drv->Open(cfstore_read_test_01_kv_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, cfstore_read_test_01_kv_data[0].key_name, cfstore_read_test_01_kv_data[0].value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + + /* seek back and forth in key and check the characters are as expected */ + node = cfstore_test_rw_data_table; + while(node->offset != CFSTORE_TEST_RW_TABLE_SENTINEL) + { + ret = cfstore_read_seek_test(hkey, node->offset, node->rw_char); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Rseek() to offset (%d) didn't read expected char (\'%c\') (ret=%d)\n", __func__, (int) node->offset, node->rw_char, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + node++; + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + return CaseNext; +} + + +/** @brief + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_read_test_02_end(const size_t call_count) +{ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_DBGLOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_read_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(200, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("READ_test_00", cfstore_read_test_00), + Case("READ_test_01_start", cfstore_utest_default_start), + Case("READ_test_01_end", cfstore_read_test_01_end), + Case("READ_test_02_start", cfstore_utest_default_start), + Case("READ_test_02_end", cfstore_read_test_02_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/write/write.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/write/write.cpp new file mode 100644 index 0000000000..41b7a5f27c --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/write/write.cpp @@ -0,0 +1,193 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file write.cpp Test cases to write KVs in the CFSTORE using the drv->Write() API call. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "Driver_Common.h" +#include "configuration_store.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#include +#include +#include + +using namespace utest::v1; + +static char cfstore_write_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.write.box1"); +UVISOR_BOX_CONFIG(cfstore_write_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* + * write tests + * cfstore_handle_t cfstore_write() + */ + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_write_test_01_kv_data[] = { + CFSTORE_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_write_test_00(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + + (void) call_count; + ret = cfstore_test_startup(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform test startup (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + return CaseNext; +} + +/** @brief test to write many times to an open KV to test data is appended + * sequentially to the end of the value blob + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_write_test_01_end(const size_t call_count) +{ + char read_buf[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint32_t i = 0; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + memset(read_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + /* create an empty KV of the required length */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_write_test_01_kv_data[0].value); + ret = cfstore_test_create(cfstore_write_test_01_kv_data[0].key_name, "one", &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create new KV (key_name=%s, ret=%d).\n", __func__, cfstore_write_test_01_kv_data[0].key_name, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + + /* now open the newly created key and write repeated to created the string */ + flags.write = true; + ret = drv->Open(cfstore_write_test_01_kv_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%d)\n", __func__, cfstore_write_test_01_kv_data[0].key_name, cfstore_write_test_01_kv_data[0].value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + + for(i = 0; i < strlen(cfstore_write_test_01_kv_data[0].value); i++) + { + len = 1; + ret = drv->Write(hkey, &cfstore_write_test_01_kv_data[0].value[i], &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Write failed for char (\'%c\') (ret=%d)\n", __func__, cfstore_write_test_01_kv_data[0].value[i], (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + } + /* check that the value created in the key is as expected*/ + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = drv->Read(hkey, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read failed (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + + /* check node key_names are identical */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: KV value (%s) is not as expected (%s).\n", __func__, read_buf, cfstore_write_test_01_kv_data[0].value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, cfstore_write_test_01_kv_data[0].value, strlen(cfstore_write_test_01_kv_data[0].value)) == 0, cfstore_write_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + + cfstore_test_delete_all(); + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + return CaseNext; +} + +/** @brief test to write with a NULL buffer, which should fail gracefully + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t cfstore_write_test_02_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + + /* create an empty KV of the required length */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_write_test_01_kv_data[0].value); + ret = cfstore_test_create(cfstore_write_test_01_kv_data[0].key_name, NULL, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error KV creation should have failed due to null write buffer but something else happended (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_INVALID_WRITE_BUFFER, cfstore_write_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + return CaseNext; +} + + +/// @cond CFSTORE_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(200, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("WRITE_test_00", cfstore_write_test_00), + Case("WRITE_test_01_start", cfstore_utest_default_start), + Case("WRITE_test_01_end", cfstore_write_test_01_end), + Case("WRITE_test_02_start", cfstore_utest_default_start), + Case("WRITE_test_02_end", cfstore_write_test_02_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/features/storage/FEATURE_STORAGE/TESTS/flash_journal/basicAPI/basicAPI.cpp b/features/storage/FEATURE_STORAGE/TESTS/flash_journal/basicAPI/basicAPI.cpp new file mode 100644 index 0000000000..b664557354 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/flash_journal/basicAPI/basicAPI.cpp @@ -0,0 +1,893 @@ +/* + * Copyright (c) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !DEVICE_STORAGE + #error [NOT_SUPPORTED] Storage not supported for this target +#endif + +#ifdef TARGET_LIKE_POSIX +#define AVOID_GREENTEA +#endif + +#ifndef AVOID_GREENTEA +#include "greentea-client/test_env.h" +#endif +#include "utest/utest.h" +#include "unity/unity.h" + +#include "flash-journal-strategy-sequential/flash_journal_strategy_sequential.h" +#include "flash-journal-strategy-sequential/flash_journal_private.h" +#include +#include + +using namespace utest::v1; + +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); +ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); + +FlashJournal_t journal; + +static const size_t BUFFER_SIZE = 8192; +static uint8_t buffer[BUFFER_SIZE]; + +static const size_t SIZEOF_SMALL_WRITE = 8; +static const size_t SIZEOF_LARGE_WRITE = BUFFER_SIZE; +static int32_t callbackStatus; + +void callbackHandler(int32_t status, FlashJournal_OpCode_t cmd_code) +{ + callbackStatus = status; + + switch (cmd_code) { + case FLASH_JOURNAL_OPCODE_INITIALIZE: + // printf("journal_callbackHandler: callback for init with status %" PRId32 "\n", status); + break; + + case FLASH_JOURNAL_OPCODE_READ_BLOB: + // printf("journal_callbackHandler: callback for read with status %" PRId32 "\n", status); + break; + + case FLASH_JOURNAL_OPCODE_LOG_BLOB: + // printf("journal_callbackHandler: callback for log with status %" PRId32 "\n", status); + break; + + case FLASH_JOURNAL_OPCODE_COMMIT: + // printf("journal_callbackHandler: callback for commit with status %" PRId32 "\n", status); + break; + + case FLASH_JOURNAL_OPCODE_RESET: + // printf("journal_callbackHandler: callback for reset with status %" PRId32 "\n", status); + break; + + default: + // printf("journal_callbackHandler: callback for opcode %u with status %" PRId32 "\n", cmd_code, status); + break; + } + Harness::validate_callback(); // Validate the callback +} + +control_t test_initialize() +{ + int32_t rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + return CaseTimeout(200); + } + + /* ensure that something got written into the memory of journal_t */ + FlashJournal_t mockJournal; + memset(&mockJournal, 0, sizeof(FlashJournal_t)); + TEST_ASSERT_NOT_EQUAL(0, memcmp(&mockJournal, &journal, sizeof(FlashJournal_t))); + + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT(info.capacity > 0); + + return CaseNext; +} + +control_t test_resetAndInitialize(const size_t call_count) +{ + int32_t rc; + FlashJournal_Info_t info; + SequentialFlashJournal_t *sequentialJournal = (SequentialFlashJournal_t *)&journal; + + static uint64_t previousCapacity; + + static enum { + NEEDS_INITIAL_RESET, + NEEDS_INITIALIZE_FOLLOWING_RESET, + NEEDS_VERIFICATION_FOLLOWING_INITIALIZE, + } state; + + printf("test_resetAndInitialize: entered with call_count %u\n", call_count); + if (call_count == 1) { + state = NEEDS_INITIAL_RESET; + } + + switch (state) { + case NEEDS_INITIAL_RESET: + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT(info.capacity > 0); + previousCapacity = info.capacity; + + printf("test_resetAndInitialize: calling reset()\n"); + rc = FlashJournal_reset(&journal); + TEST_ASSERT_NOT_EQUAL(JOURNAL_STATUS_UNSUPPORTED, rc); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + state = NEEDS_INITIALIZE_FOLLOWING_RESET; + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(1000) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of reset() is expected to return 1 */ + + /* fall through */ + case NEEDS_INITIALIZE_FOLLOWING_RESET: + /* ensure that the journal has been re-initialized */ + TEST_ASSERT_EQUAL(0, sequentialJournal->nextSequenceNumber); + TEST_ASSERT_EQUAL((uint32_t)-1, sequentialJournal->currentBlobIndex); + TEST_ASSERT_EQUAL(SEQUENTIAL_JOURNAL_STATE_INITIALIZED, sequentialJournal->state); + + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT(info.capacity > 0); + TEST_ASSERT_EQUAL(previousCapacity, info.capacity); + TEST_ASSERT_EQUAL(0, info.sizeofJournaledBlob); + + /* attempt an initialize following reset() */ + printf("test_resetAndInitialize: calling initialize() after reset\n"); + rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + state = NEEDS_VERIFICATION_FOLLOWING_INITIALIZE; + if (rc == JOURNAL_STATUS_OK) { + return CaseTimeout(200); + } + TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */ + + /* fall through */ + case NEEDS_VERIFICATION_FOLLOWING_INITIALIZE: + default: + printf("test_resetAndInitialize: verification\n"); + TEST_ASSERT_EQUAL(0, sequentialJournal->nextSequenceNumber); + TEST_ASSERT_EQUAL((uint32_t)-1, sequentialJournal->currentBlobIndex); + TEST_ASSERT_EQUAL(SEQUENTIAL_JOURNAL_STATE_INITIALIZED, sequentialJournal->state); + + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT(info.capacity > 0); + TEST_ASSERT_EQUAL(previousCapacity, info.capacity); + TEST_ASSERT_EQUAL(0, info.sizeofJournaledBlob); + break; + } + + return CaseNext; +} + +control_t test_commitWithoutLogs(const size_t call_count) +{ + int32_t rc; + + printf("test_commitWithoutLogs: entered with call_count %u\n", call_count); + + switch (call_count) { + case 1: + /* initialize */ + rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + return CaseTimeout(200) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */ + return CaseRepeatAll; + + case 2: + rc = FlashJournal_commit(&journal); + // printf("commit returned %" PRId32 "\r\n", rc); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + /* intentional fall through*/ + callbackStatus = rc; + + case 3: + TEST_ASSERT_EQUAL(1, callbackStatus); + break; + } + + return CaseNext; +} + +control_t test_logSmallWithoutCommit(const size_t call_count) +{ + int32_t rc; + + printf("test_logSmallWithoutCommit: entered with call_count %u\n", call_count); + + switch (call_count) { + case 1: + /* initialize */ + rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + return CaseTimeout(200) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */ + return CaseRepeatAll; + + case 2: + /* log without commit */ + memset(buffer, 0xAA, SIZEOF_SMALL_WRITE); + rc = FlashJournal_log(&journal, buffer, SIZEOF_SMALL_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + /* else, fall through to synchronous verification */ + + default: + rc = FlashJournal_read(&journal, buffer, SIZEOF_SMALL_WRITE); + TEST_ASSERT(rc < JOURNAL_STATUS_OK); + return CaseNext; + } +} + +template +control_t test_logSmallAndCommit(const size_t call_count) +{ + int32_t rc; + + printf("test_logSmallAndCommit: entered with call_count %u\n", call_count); + + switch (call_count) { + case 1: + memset(buffer, PATTERN, SIZEOF_SMALL_WRITE); + rc = FlashJournal_log(&journal, buffer, SIZEOF_SMALL_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + /* else, fall through to synchronous verification */ + + case 2: + rc = FlashJournal_commit(&journal); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + /* else, fall through to synchronous verification */ + + case 3: + { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_SMALL_WRITE, info.sizeofJournaledBlob); + } + + rc = FlashJournal_read(&journal, buffer, SIZEOF_SMALL_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + TEST_ASSERT_EQUAL(SIZEOF_SMALL_WRITE, rc); + /* intentional fall-through */ + + default: + for (unsigned i = 0; i < SIZEOF_SMALL_WRITE; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL(PATTERN, buffer[i]); + } + + return CaseNext; + } +} + +control_t test_initializeAfterLogSmallAndCommit(const size_t call_count) +{ + int32_t rc; + + printf("test_initializeAfterLogSmallAndCommit: entered with call_count %u\n", call_count); + + if (call_count == 1) { + rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + printf("asynchronous_ops for init\n"); + return CaseTimeout(200) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */ + } + + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_SMALL_WRITE, info.sizeofJournaledBlob); + + return CaseNext; +} + +control_t test_logLargeWithoutCommit(const size_t call_count) +{ + int32_t rc; + + printf("test_logLargeWithoutCommit: entered with call_count %u\n", call_count); + + switch (call_count) { + case 1: + rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + return CaseTimeout(200) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */ + return CaseRepeatAll; + + case 2: + memset(buffer, 0xAA, SIZEOF_LARGE_WRITE); + rc = FlashJournal_log(&journal, buffer, SIZEOF_LARGE_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(5000) + CaseRepeatAll; + } + /* intentional fall-through */ + + case 3: + default: + rc = FlashJournal_read(&journal, buffer, SIZEOF_LARGE_WRITE); + TEST_ASSERT(rc < JOURNAL_STATUS_OK); + return CaseNext; + } +} + +template +control_t test_logLargeAndCommit(const size_t call_count) +{ + int32_t rc; + + printf("test_logLargeAndCommit: entered with call_count %u\n", call_count); + + switch (call_count) { + case 1: + memset(buffer, PATTERN, SIZEOF_LARGE_WRITE); + rc = FlashJournal_log(&journal, buffer, SIZEOF_LARGE_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + /* intentional fall-through */ + + case 2: + rc = FlashJournal_commit(&journal); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + /* intentional fall-through */ + + case 3: + { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob); + } + + rc = FlashJournal_read(&journal, buffer, SIZEOF_LARGE_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, rc); + /* intentional fall-through */ + + default: + for (unsigned i = 0; i < SIZEOF_LARGE_WRITE; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL(PATTERN, buffer[i]); + } + + return CaseNext; + } +} + +control_t test_initializeAfterLogLargeAndCommit(const size_t call_count) +{ + int32_t rc; + + printf("test_initializeAfterLogLargeAndCommit: entered with call_count %u\n", call_count); + + if (call_count == 1) { + rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + printf("test_initializeAfterLogLargeAndCommit: asynchronous_ops for init\n"); + return CaseTimeout(200) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */ + } + + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob); + + return CaseNext; +} + +template +control_t test_logLargeAndReadSmallChunks(const size_t call_count) +{ + int32_t rc; + + printf("test_logLargeAndReadSmallChunks: entered with call_count %u\n", call_count); + + static const size_t SMALL_CHUNK_COUNT = 4; + + switch (call_count) { + case 1: + memset(buffer, PATTERN, SIZEOF_LARGE_WRITE); + rc = FlashJournal_log(&journal, buffer, SIZEOF_LARGE_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + /* intentional fall-through */ + + case 2: + rc = FlashJournal_commit(&journal); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + /* intentional fall-through */ + + case 3: + { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob); + } + /* intentional fall-through */ + + default: + break; + } + + if (call_count > 3) { + if (drv->GetCapabilities().asynchronous_ops) { + if (callbackStatus == 0) { + return CaseNext; /* termination condition */ + } + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE / SMALL_CHUNK_COUNT, callbackStatus); + } + + for (unsigned i = 0; i < SIZEOF_LARGE_WRITE / SMALL_CHUNK_COUNT; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL(PATTERN, buffer[i]); + } + } + + while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_LARGE_WRITE / SMALL_CHUNK_COUNT)) != JOURNAL_STATUS_EMPTY) { + // printf("read returned %ld\n", rc); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE / SMALL_CHUNK_COUNT, rc); + for (unsigned i = 0; i < SIZEOF_LARGE_WRITE / SMALL_CHUNK_COUNT; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL(PATTERN, buffer[i]); + } + }; + + return CaseNext; +} + +template +control_t test_readLargeInSmallOddChunks(const size_t call_count) +{ + int32_t rc; + + printf("test_readLargeInSmallOddChunks<0x%02x, %u>: entered with call_count %u\n", PATTERN, SIZEOF_READS, call_count); + + if (call_count == 1) { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob); + TEST_ASSERT(SIZEOF_READS < info.sizeofJournaledBlob); + } else { + if (drv->GetCapabilities().asynchronous_ops) { + if (callbackStatus == 0) { + return CaseNext; /* termination condition */ + } + TEST_ASSERT_EQUAL(SIZEOF_READS, callbackStatus); + } + + for (unsigned i = 0; i < SIZEOF_READS; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL(PATTERN, buffer[i]); + } + } + + while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_READS)) != JOURNAL_STATUS_EMPTY) { + // printf("read returned %ld\n", rc); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + TEST_ASSERT(rc <= (int32_t)SIZEOF_READS); + for (unsigned i = 0; i < (unsigned)rc; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL(PATTERN, buffer[i]); + } + }; + + return CaseNext; +} + +template +control_t test_logSeveralOddSizedChunks(size_t call_count) +{ + TEST_ASSERT(N_WRITES >= 1); + + int32_t rc; + + static const uint8_t PATTERN = 0xAA; + static size_t totalDataLogged = 0; + + printf("test_logSeveralOddSizedChunks<%u, %u>: entered with call_count %u\n", SIZEOF_ODD_CHUNK, N_WRITES, call_count); + TEST_ASSERT(SIZEOF_ODD_CHUNK <= BUFFER_SIZE); + + /* check the status of the previous asynchronous operation */ + if ((call_count > 1) && (call_count <= (N_WRITES + 1))) { + TEST_ASSERT((callbackStatus >= JOURNAL_STATUS_OK) || (callbackStatus == JOURNAL_STATUS_SMALL_LOG_REQUEST)); + if (callbackStatus == JOURNAL_STATUS_SMALL_LOG_REQUEST) { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT(SIZEOF_ODD_CHUNK < info.program_unit); + printf("test_logSeveralOddSizedChunks: RETURNING CaseNext\n"); + return CaseNext; + } + + size_t sizeofLoggedData = callbackStatus; + TEST_ASSERT((size_t)sizeofLoggedData <= SIZEOF_ODD_CHUNK); + if (sizeofLoggedData < SIZEOF_ODD_CHUNK) { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT((sizeofLoggedData % info.program_unit) == 0); + } + totalDataLogged += sizeofLoggedData; + } + + while (call_count <= N_WRITES) { + printf("test_logSeveralOddSizedChunks: iteration with call_count %u\n", call_count); + memset(buffer, PATTERN, SIZEOF_ODD_CHUNK); + rc = FlashJournal_log(&journal, buffer, SIZEOF_ODD_CHUNK); + // printf("test_logSeveralOddSizedChunks: called FlashJournal_log(): rc = %" PRId32 "\n", rc); + TEST_ASSERT((rc >= JOURNAL_STATUS_OK) || (rc == JOURNAL_STATUS_SMALL_LOG_REQUEST)); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + if (rc == JOURNAL_STATUS_SMALL_LOG_REQUEST) { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT(SIZEOF_ODD_CHUNK < info.program_unit); + return CaseNext; + } + + size_t sizeofLoggedData = rc; + TEST_ASSERT(sizeofLoggedData <= SIZEOF_ODD_CHUNK); /* the amount actually written is expected to be less than the original */ + if (sizeofLoggedData < SIZEOF_ODD_CHUNK) { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT((sizeofLoggedData % info.program_unit) == 0); + } + + totalDataLogged += sizeofLoggedData; + ++call_count; /* simulate CaseRepeatAll for the synchronous case */ + } + + if (call_count == (N_WRITES + 1)) { + rc = FlashJournal_commit(&journal); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + callbackStatus = rc; + } + + TEST_ASSERT_EQUAL(1, callbackStatus); + { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(totalDataLogged, info.sizeofJournaledBlob); + } + + return CaseNext; +} + +control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_t call_count) +{ + int32_t rc; + + static const uint8_t PATTERN = 0xAA; + static const size_t N_WRITES = 4; + static const size_t N_READS = N_WRITES; + static const size_t SIZEOF_WRITE = BUFFER_SIZE / N_WRITES; + static const size_t SIZEOF_READ = BUFFER_SIZE / N_READS; + + printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: entered with call_count %u\n", call_count); + + if (call_count <= N_WRITES) { + printf("writing pattern %02x\n", PATTERN ^ call_count); + memset(buffer, (PATTERN ^ call_count), SIZEOF_WRITE); + rc = FlashJournal_log(&journal, buffer, SIZEOF_WRITE); + // printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: log returned %" PRId32 "\n", rc); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(SIZEOF_WRITE, rc); + return CaseRepeatAll; + } else if (call_count == (N_WRITES + 1)) { + rc = FlashJournal_commit(&journal); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + // printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: commit returned %" PRId32 "\n", rc); + callbackStatus = rc; /* pass forward the return value so that the next iteration can check callbackStatus */ + return CaseRepeatAll; + } else if (call_count < (N_WRITES + 1 + N_READS + 1)) { + unsigned readIteration = call_count - (N_WRITES + 1); + printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read iteration %u\n", readIteration); + if (call_count == (N_WRITES + 1 /* commit */ + 1 /* first iteration after commit */)) { + TEST_ASSERT_EQUAL(1, callbackStatus); + + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(BUFFER_SIZE, info.sizeofJournaledBlob); + } else { + TEST_ASSERT_EQUAL(SIZEOF_READ, callbackStatus); + for (unsigned i = 0; i < SIZEOF_READ; i++) { + // printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL(PATTERN ^ (readIteration - 1), buffer[i]); + } + } + + while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_READ)) != JOURNAL_STATUS_EMPTY) { + // printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read returned %ld\n", rc); + TEST_ASSERT((rc == JOURNAL_STATUS_OK) || (rc == SIZEOF_READ)); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + TEST_ASSERT_EQUAL(SIZEOF_READ, rc); + printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: checking for pattern %02x\n", PATTERN ^ readIteration); + for (unsigned i = 0; i < SIZEOF_READ; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL(PATTERN ^ readIteration, buffer[i]); + } + ++readIteration; + }; + TEST_ASSERT_EQUAL(N_READS + 1, readIteration); + } + + return CaseNext; +} + +control_t test_failedSmallWriteFollowedByPaddedWrite(const size_t call_count) +{ + int32_t rc; + + static const uint8_t PATTERN = 0xAA; + + printf("test_failedSmallWriteFollowedByPaddedWrite: entered with call_count %u\n", call_count); + + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT(info.program_unit >= 1); + if (info.program_unit == 1) { + return CaseNext; + } + + static const size_t SMALL_CONSTANT = 8 * info.program_unit; + static const size_t SIZEOF_WRITE = (info.program_unit - 1) + SMALL_CONSTANT; + TEST_ASSERT(SIZEOF_WRITE <= BUFFER_SIZE); + + memset(buffer, PATTERN, SIZEOF_WRITE); + + if (call_count == 1) { + rc = FlashJournal_log(&journal, buffer, SIZEOF_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(SMALL_CONSTANT, rc); + callbackStatus = rc; + return CaseRepeatAll; + } else if (call_count == 2) { + TEST_ASSERT_EQUAL(SMALL_CONSTANT, callbackStatus); + rc = FlashJournal_log(&journal, buffer, SIZEOF_WRITE - callbackStatus); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_SMALL_LOG_REQUEST, rc); + + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT(info.program_unit >= 1); + TEST_ASSERT(info.program_unit <= BUFFER_SIZE); + + rc = FlashJournal_log(&journal, buffer, info.program_unit); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(info.program_unit, rc); + callbackStatus = rc; + return CaseRepeatAll; + } else if (call_count == 3) { + rc = FlashJournal_commit(&journal); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(1, rc); + callbackStatus = rc; + return CaseRepeatAll; + } else { + TEST_ASSERT_EQUAL(1, callbackStatus); + + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL((SIZEOF_WRITE + 1), info.sizeofJournaledBlob); + } + + return CaseNext; +} + +#ifndef AVOID_GREENTEA +// Custom setup handler required for proper Greentea support +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(60, "default_auto"); + // Call the default reporting function + return greentea_test_setup_handler(number_of_cases); +} +#else +status_t default_setup(const size_t) +{ + return STATUS_CONTINUE; +} +#endif + +// Specify all your test cases here +Case cases[] = { + Case("initialize", test_initialize), + Case("reset and initialize1", test_resetAndInitialize), + + Case("log small item without commit", test_logSmallWithoutCommit), + Case("reset and initialize2", test_resetAndInitialize), + + Case("commit without logs", test_commitWithoutLogs), + Case("initialize", test_initialize), + + /* log small item, and reinitialize */ + Case("log small item and commit1", test_logSmallAndCommit<0xAA>), + Case("initialize after small log and commit1", test_initializeAfterLogSmallAndCommit), + Case("log small item and commit2", test_logSmallAndCommit<0x11>), + Case("initialize after small log and commit2", test_initializeAfterLogSmallAndCommit), + Case("log small item and commit3", test_logSmallAndCommit<0x22>), + Case("initialize after small log and commit3", test_initializeAfterLogSmallAndCommit), + Case("log small item and commit4", test_logSmallAndCommit<0x55>), + Case("initialize after small log and commit4", test_initializeAfterLogSmallAndCommit), + Case("log small item and commit5", test_logSmallAndCommit<0xAB>), + Case("initialize after small log and commit5", test_initializeAfterLogSmallAndCommit), + Case("reset and initialize3", test_resetAndInitialize), + + Case("log large item without commit", test_logLargeWithoutCommit), + + /* initialize, log large item, and reinitialize */ + Case("initialize2", test_initialize), + Case("reset and initialize4", test_resetAndInitialize), + Case("log large item and commit1", test_logLargeAndCommit<0xAA>), + Case("initialize after large log and commit1", test_initializeAfterLogLargeAndCommit), + Case("log large item and commit2", test_logLargeAndCommit<0x55>), + Case("initialize after large log and commit2", test_initializeAfterLogLargeAndCommit), + Case("log large item and commit3", test_logLargeAndCommit<0x11>), + Case("initialize after large log and commit3", test_initializeAfterLogLargeAndCommit), + Case("log large item and commit4", test_logLargeAndCommit<0xAB>), + Case("initialize after large log and commit4", test_initializeAfterLogLargeAndCommit), + Case("log large item and commit5", test_logLargeAndCommit<0x22>), + Case("initialize after large log and commit5", test_initializeAfterLogLargeAndCommit), + Case("reset and initialize5", test_resetAndInitialize), + + Case("log large item and read smaller chunks", test_logLargeAndReadSmallChunks<0xAA>), + Case("read large item in small, odd-sized chunks1", test_readLargeInSmallOddChunks<0xAA, ((BUFFER_SIZE / 2) - 1)>), + Case("read large item in small, odd-sized chunks2", test_readLargeInSmallOddChunks<0xAA, 255>), + Case("read large item in small, odd-sized chunks3", test_readLargeInSmallOddChunks<0xAA, 1021>), + Case("read large item in small, odd-sized chunks4", test_readLargeInSmallOddChunks<0xAA, 2401>), + + /* log odd-sized blocks which wouldn't align with program_unit at the tail */ + Case("initialize3", test_initialize), + Case("log odd-sized chunk", test_logSeveralOddSizedChunks<1, 1>), + Case("log odd-sized chunk", test_logSeveralOddSizedChunks<101, 11>), + Case("log odd-sized chunk", test_logSeveralOddSizedChunks<1217, 4>), + Case("log odd-sized chunk", test_logSeveralOddSizedChunks<2402, 5>), + Case("log odd-sized chunk", test_logSeveralOddSizedChunks<4803, 3>), + Case("log odd-sized chunk", test_logSeveralOddSizedChunks<(BUFFER_SIZE-1), 7>), + + Case("initialize4", test_initialize), + Case("multiple writes, commit, multiple reads", test_multipleWritesFollowedByCommitFollowedByMultipleReads), + + Case("failed small write followed by padded write", test_failedSmallWriteFollowedByPaddedWrite), + + Case("reset and initialize6", test_resetAndInitialize), + // Case("uninitialize", test_uninitialize), +}; + +// Declare your test specification with a custom setup handler +#ifndef AVOID_GREENTEA +Specification specification(greentea_setup, cases); +#else +Specification specification(default_setup, cases); +#endif + +int main(int argc, char** argv) +{ + // Run the test specification + Harness::run(specification); +} diff --git a/features/storage/FEATURE_STORAGE/cfstore/LICENSE b/features/storage/FEATURE_STORAGE/cfstore/LICENSE new file mode 100644 index 0000000000..3ebeebb336 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/LICENSE @@ -0,0 +1,8 @@ +Unless specifically indicated otherwise in a file, files are licensed +2 under the Apache 2.0 license, as can be found in: apache-2.0.txt + +Source code in cfstore_fnmatch.c is licensed under the agreements in the +following files: +- berkeley.txt +- chernov.txt +- tatmanjants.txt \ No newline at end of file diff --git a/features/storage/FEATURE_STORAGE/cfstore/Makefile.scripts b/features/storage/FEATURE_STORAGE/cfstore/Makefile.scripts new file mode 100644 index 0000000000..b9067d61a7 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/Makefile.scripts @@ -0,0 +1,65 @@ +########################################################################### +# +# Copyright (c) 2013-2015, ARM Limited, All Rights Reserved +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +# +# inline debugging/flashing scripts +# +define __SCRIPT_GDB + target remote $(DEBUG_HOST) + monitor endian little + monitor reset + monitor halt + monitor semihosting enable + monitor speed 1000 + monitor loadbin $(TARGET_BIN) 0 + monitor flash device = $(CPU) + load $(TARGET) + file $(TARGET) + $(GDB_DEBUG_UVISOR) + b vmpu_init_post + b app_start +endef +export __SCRIPT_GDB + +define __SCRIPT_FLASH + r + loadbin $(TARGET_BIN) 0 + r + g + q +endef +export __SCRIPT_FLASH + +define __SCRIPT_ERASE + h + Sleep 100 + unlock kinetis + Sleep 100 + erase + q +endef +export __SCRIPT_ERASE + +define __SCRIPT_RESET + h + r + g + q +endef +export __SCRIPT_RESET diff --git a/features/storage/FEATURE_STORAGE/cfstore/README.md b/features/storage/FEATURE_STORAGE/cfstore/README.md new file mode 100644 index 0000000000..eb7773253b --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/README.md @@ -0,0 +1,103 @@ +# Secure Key-Value Storage # + + +## Executive Summary + +The Configuration Store (CFSTORE) is a secure, +associative key-value (KV) store C-Language Hardware Abstraction Layer. +CFSTORE provides the secure and persistent storage for: +- Storing encryption keys data. +- Storing configuration data. +- Storing firmware, firmware updates and incremental firmware blocks for assembling into a firmware update. + +These services are presented to clients with: +- A conceptually simple, file-like interface for storing and managing data using (key, value) pairs in + persistent storage media. +- A simple, hardware-independent API to promote portability across multiple platforms and a low attack surface. +- A very small code/memory footprint so CFSTORE is capable of running on highly-constrained memory systems (~10kB free memory) + where typically available SRAM << NV storage. +- A simple (low complexity) storage capability at the expense of features. For example, CFSTORE only supports the storage of + binary blobs rather than a rich set of data types. + +Current support includes: +- NV-backed support. Integration with Flash Abstraction (Flash Journal Strategy Sequential) for persistent storage on the Freescale FRDM K64F target. +- SRAM backed support. +- More than 60 test cases with >80% test coverage. +- Comprehensive documentation including doxygen generated API and test case documentation. + + +# Configuration-Store Software Architecture + +```C + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Configuration Store Client | + | e.g. FOTA | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Configuration Store | | uvisor | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | Flash Abstraction Layer | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | Flash Driver Layer | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + SW + ----------------------------------------------------------------------- + HW + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NV Storage Media e.g. Flash | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Configuration Store Software Architecture + +``` + +The above figure shows the following entities (from top to bottom): +- A Configuration Store client e.g. FOTA. +- Configuration Store, the associative KV pair store. +- Flash Abstraction Layer, portable across the driver layer. +- Flash Driver layer e.g. CMSIS-Driver. +- NV Storage Media. These are the physical storage media. + + +# Providing Feedback + +If you would like to make a contribution to CFSTORE, please provide feedback/designs/comments/code in one of the following ways: +- By logging an issue in the CFSTORE repo. +- By submitting a Pull Request to the CFSTORE repo. +- By sending an email to: +-- simon.hughes@arm.com +-- milosch.meriac@arm.com + + +# Further Reading + +* The [CFSTORE Getting Started Guide][CFSTORE_GETSTART] including examples. +* The [CFSTORE Engineering Requirements.][CFSTORE_ENGREQ] +* The [CFSTORE High Level Design Document.][CFSTORE_HLD] +* The [CFSTORE Low Level Design Document.][CFSTORE_LLD] +* The [CFSTORE Test Plan.][CFSTORE_TESTPLAN] +* The [CFSTORE Application Note 0001: NXP Freescale Kinetis FRDM-K64F Flash Memory Usage.][CFSTORE_APPNOTE_0001] +* The [CFSTORE Releases][CFSTORE_RELEASE] provides requirements tracking by listing the requirements supported for a CFSTORE version. + + +20160714 + +[CFSTORE_ENGREQ]: doc/design/configuration_store_requirements.md +[CFSTORE_EX1]: ../TESTS/cfstore/example1/example1.cpp +[CFSTORE_EX3]: ../TESTS/cfstore/example3/example3.cpp +[CFSTORE_GETSTART]: doc/design/configuration_store_getting_started.md +[CFSTORE_HLD]: doc/design/configuration_store_hld.md +[CFSTORE_LLD]: doc/design/configuration_store_lld.md +[CFSTORE_TESTPLAN]: doc/design/configuration_store_test_plan.md +[CFSTORE_PROJPLAN]:doc/design/configuration_store_project.md +[CFSTORE_RELEASE]: doc/project/configuration_store_releases.md +[CFSTORE_TERM]: doc/design/configuration_store_terminology.md +[CFSTORE_APPNOTE_0001]: doc/design/configuration_store_app_note_0001.md \ No newline at end of file diff --git a/features/storage/FEATURE_STORAGE/cfstore/VERSION b/features/storage/FEATURE_STORAGE/cfstore/VERSION new file mode 100644 index 0000000000..a3c7a02f9b --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/VERSION @@ -0,0 +1,3 @@ +{ + "version": "0.3.3", +} diff --git a/features/storage/FEATURE_STORAGE/cfstore/apache-2.0.txt b/features/storage/FEATURE_STORAGE/cfstore/apache-2.0.txt new file mode 100644 index 0000000000..35750c5de1 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/apache-2.0.txt @@ -0,0 +1,13 @@ +Copyright (c) 2016 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/features/storage/FEATURE_STORAGE/cfstore/berkeley.txt b/features/storage/FEATURE_STORAGE/cfstore/berkeley.txt new file mode 100644 index 0000000000..85d6eacfe0 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/berkeley.txt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ \ No newline at end of file diff --git a/features/storage/FEATURE_STORAGE/cfstore/cfstore.doxyfile b/features/storage/FEATURE_STORAGE/cfstore/cfstore.doxyfile new file mode 100644 index 0000000000..cfe73ceb19 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/cfstore.doxyfile @@ -0,0 +1,2419 @@ +# Doxyfile 1.8.10 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Configuration Store" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = v0.3.3 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Secure Associative Key-Value Store for Persisting Data Attributes to Flash in mbedOS Based Systems ." + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = apidoc/ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = "experimental=\n
Experimental:
This feature is marked as experimental.
\n" \ + "experimental{1}=\n
Experimental:
\1
\n" + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = h=C + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = YES + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = doxygen_warn.log + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = configuration-store README.md doc/design/ doc/project/ ../TESTS/cfstore + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, +# *.vhdl, *.ucf, *.qsf, *.as and *.js. + +FILE_PATTERNS = *.h \ + *.cpp \ + *.md + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = configs \ + CONTRIBUTING.md + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = doc/design/pics + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +#USE_MDFILE_AS_MAINPAGE = DOXYGEN_FRONTPAGE.md +USE_MDFILE_AS_MAINPAGE = README.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = . + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /