/* * 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 "mbed_stats.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" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <inttypes.h> using namespace utest::v1; #ifdef CFSTORE_DEBUG #define CFSTORE_CREATE_GREENTEA_TIMEOUT_S 360 #else #define CFSTORE_CREATE_GREENTEA_TIMEOUT_S 60 #endif #define CFSTORE_CREATE_MALLOC_SIZE 1024 static char cfstore_create_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; /// @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, double the value blob size. * - check all the cfstore entries can be read correctly and their * data agrees with the data supplied upon creation. * - shrink the mid-entry value blob size to be ~half 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 Support function for test cases 05 * * Create enough KV's to consume the whole of available memory */ int32_t cfstore_create_test_05_core(const size_t call_count) { int32_t ret = ARM_DRIVER_ERROR; uint32_t i = 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__); /* Initialize() */ cfstore_utest_default_start(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)); if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ CFSTORE_ERRLOG("Out of memory on %d-th KV.\n", (int) i); break; } /* revert CFSTORE_LOG for more trace */ CFSTORE_DBGLOG("Successfully stored %d-th KV.\n", (int) i); } 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 ret; } /** * @brief Test case to check cfstore recovers from out of memory * errors without leaking memory * * This test case does the following: * 1. Start loop. * 2. Initializes CFSTORE. * 3. Creates as many KVs as required to run out of memory. The number of bytes B * allocated before running out of memory is recorded. * 4. For loop i, check that B_i = B_i-1 for i>0 i.e. that no memory has been leaked * 5. Uninitialize(), which should clean up any cfstore internal state including * freeing the internal memeory area. * 6. Repeat from step 1 N times. */ control_t cfstore_create_test_05(const size_t call_count) { uint32_t i = 0; int32_t ret = ARM_DRIVER_ERROR; const uint32_t max_loops = 50; mbed_stats_heap_t stats_before; mbed_stats_heap_t stats_after; mbed_stats_heap_get(&stats_before); CFSTORE_FENTRYLOG("%s:entered\n", __func__); for(i = 0; i < max_loops; i++) { ret = cfstore_create_test_05_core(call_count); CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: cfstore_create_test_05_core() failed (ret = %d.\n", __func__, (int) ret); TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); mbed_stats_heap_get(&stats_after); if(i > 1) { CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: memory leak: stored %d bytes on loop %d, but %d bytes on loop %d .\n", __func__, (int) stats_after.current_size, (int) i, (int) stats_before.current_size, (int) i-1); TEST_ASSERT_MESSAGE(stats_after.current_size == stats_before.current_size, cfstore_create_utest_msg_g); TEST_ASSERT(stats_after.alloc_fail_cnt > stats_before.alloc_fail_cnt); } stats_before = stats_after; } 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; } /** @brief Test case to change the value blob size of pre-existing * key in a way that causes the memory area to realloc-ed, * * The test is a modified version of cfstore_create_test_01_end which * - creates KVs, * - mallocs a memory object on the heap * - increases the size of one of the existing KVs, causing the * internal memory area to be realloc-ed. * * In detail, the test does the following: * 1. creates a cfstore with ~10 entries. This causes the configuration * store to realloc() heap memory for KV storage. * 2. mallocs a memory object on heap. * 3. for a mid-cfstore entry, double the value blob size. This will cause the * cfstore memory area to be realloced. * 4. check all the cfstore entries can be read correctly and their * data agrees with the data supplied upon creation. * 5. shrink the mid-entry value blob size to be ~half 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_07_end(const size_t call_count) { int32_t ret = ARM_DRIVER_ERROR; void *test_buf1 = NULL; 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)); /* step 1 */ 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); /* step 2 */ test_buf1 = malloc(CFSTORE_CREATE_MALLOC_SIZE); CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to allocate memory (test_buf1=%p)\n", __func__, test_buf1); TEST_ASSERT_MESSAGE(test_buf1 != NULL, cfstore_create_utest_msg_g); /* step 3. 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); /* step 4. 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); /* Step 5. 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++; } free(test_buf1); 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; } /// @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), #if defined(MBED_HEAP_STATS_ENABLED) && MBED_HEAP_STATS_ENABLED && !defined(__ICCARM__) Case("CREATE_test_05", cfstore_create_test_05), #endif Case("CREATE_test_06_start", cfstore_utest_default_start), Case("CREATE_test_06_end", cfstore_create_test_06_end), Case("CREATE_test_07_start", cfstore_utest_default_start), Case("CREATE_test_07_end", cfstore_create_test_07_end), }; /* Declare your test specification with a custom setup handler */ Specification specification(greentea_setup, cases); int main() { return !Harness::run(specification); } /// @endcond