From c93626db3f92e03ee781e6617124078bed832ce5 Mon Sep 17 00:00:00 2001 From: konstantin Date: Tue, 20 Feb 2018 12:11:56 +0500 Subject: [PATCH] nvs:Add functions for calculating used/free entries Users needs functions to count the number of free and used entries. 1. `nvs_get_stats()` This function return structure of statistic about the uspace NVS. (Struct: used_entries, free_entries, total_entries and namespace_count) 2. `nvs_get_used_entry_count()` The second function return amount of entries in the namespace (by handler) 3. Added unit tests. Closes TW<12282> --- components/nvs_flash/include/nvs.h | 82 ++++++++++ components/nvs_flash/src/nvs_api.cpp | 46 ++++++ components/nvs_flash/src/nvs_page.cpp | 29 ++++ components/nvs_flash/src/nvs_page.hpp | 2 + components/nvs_flash/src/nvs_pagemanager.cpp | 22 +++ components/nvs_flash/src/nvs_pagemanager.hpp | 2 + components/nvs_flash/src/nvs_storage.cpp | 33 ++++ components/nvs_flash/src/nvs_storage.hpp | 3 + components/nvs_flash/test/test_nvs.c | 148 ++++++++++++++++++ .../nvs_flash/test_nvs_host/test_nvs.cpp | 142 ++++++++++++++++- 10 files changed, 505 insertions(+), 4 deletions(-) diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index dfdd18c92..c58f62f9c 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -356,6 +356,88 @@ esp_err_t nvs_commit(nvs_handle handle); */ void nvs_close(nvs_handle handle); +/** + * @note Info about storage space NVS. + */ +typedef struct { + size_t used_entries; /**< Amount of used entries. */ + size_t free_entries; /**< Amount of free entries. */ + size_t total_entries; /**< Amount all available entries. */ + size_t namespace_count; /**< Amount name space. */ +} nvs_stats_t; + +/** + * @brief Fill structure nvs_stats_t. It provides info about used memory the partition. + * + * This function calculates to runtime the number of used entries, free entries, total entries, + * and amount namespace in partition. + * + * \code{c} + * // Example of nvs_get_stats() to get the number of used entries and free entries: + * nvs_stats_t nvs_stats; + * nvs_get_stats(NULL, &nvs_stats); + * printf("Count: UsedEntries = (%d), FreeEntries = (%d), AllEntries = (%d)\n", + nvs_stats.used_entries, nvs_stats.free_entries, nvs_stats.total_entries); + * \endcode + * + * @param[in] part_name Partition name NVS in the partition table. + * If pass a NULL than will use NVS_DEFAULT_PART_NAME ("nvs"). + * + * @param[out] nvs_stats Returns filled structure nvs_states_t. + * It provides info about used memory the partition. + * + * + * @return + * - ESP_OK if the changes have been written successfully. + * Return param nvs_stats will be filled. + * - ESP_ERR_NVS_PART_NOT_FOUND if the partition with label "name" is not found. + * Return param nvs_stats will be filled 0. + * - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized. + * Return param nvs_stats will be filled 0. + * - ESP_ERR_INVALID_ARG if nvs_stats equal to NULL. + * - ESP_ERR_INVALID_STATE if there is page with the status of INVALID. + * Return param nvs_stats will be filled not with correct values because + * not all pages will be counted. Counting will be interrupted at the first INVALID page. + */ +esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats); + +/** + * @brief Calculate all entries in a namespace. + * + * Note that to find out the total number of records occupied by the namespace, + * add one to the returned value used_entries (if err is equal to ESP_OK). + * Because the name space entry takes one entry. + * + * \code{c} + * // Example of nvs_get_used_entry_count() to get amount of all key-value pairs in one namespace: + * nvs_handle handle; + * nvs_open("namespace1", NVS_READWRITE, &handle); + * ... + * size_t used_entries; + * size_t total_entries_namespace; + * if(nvs_get_used_entry_count(handle, &used_entries) == ESP_OK){ + * // the total number of records occupied by the namespace + * total_entries_namespace = used_entries + 1; + * } + * \endcode + * + * @param[in] handle Handle obtained from nvs_open function. + * + * @param[out] used_entries Returns amount of used entries from a namespace. + * + * + * @return + * - ESP_OK if the changes have been written successfully. + * Return param used_entries will be filled valid value. + * - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized. + * Return param used_entries will be filled 0. + * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL. + * Return param used_entries will be filled 0. + * - ESP_ERR_INVALID_ARG if nvs_stats equal to NULL. + * - Other error codes from the underlying storage driver. + * Return param used_entries will be filled 0. + */ +esp_err_t nvs_get_used_entry_count(nvs_handle handle, size_t* used_entries); #ifdef __cplusplus } // extern "C" diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 15f76d1dd..cb23a4fc9 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -458,3 +458,49 @@ extern "C" esp_err_t nvs_get_blob(nvs_handle handle, const char* key, void* out_ return nvs_get_str_or_blob(handle, nvs::ItemType::BLOB, key, out_value, length); } +extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats) +{ + Lock lock; + nvs::Storage* pStorage; + + if (nvs_stats == NULL) { + return ESP_ERR_INVALID_ARG; + } + nvs_stats->used_entries = 0; + nvs_stats->free_entries = 0; + nvs_stats->total_entries = 0; + nvs_stats->namespace_count = 0; + + pStorage = lookup_storage_from_name((part_name == NULL) ? NVS_DEFAULT_PART_NAME : part_name); + if (pStorage == NULL) { + return ESP_ERR_NVS_PART_NOT_FOUND; + } + + if(!pStorage->isValid()){ + return ESP_ERR_NVS_NOT_INITIALIZED; + } + + return pStorage->fillStats(*nvs_stats); +} + +extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle handle, size_t* used_entries) +{ + Lock lock; + if(used_entries == NULL){ + return ESP_ERR_INVALID_ARG; + } + *used_entries = 0; + + HandleEntry entry; + auto err = nvs_find_ns_handle(handle, entry); + if (err != ESP_OK) { + return err; + } + + size_t used_entry_count; + err = entry.mStoragePtr->calcEntriesInNamespace(entry.mNsIndex, used_entry_count); + if(err == ESP_OK){ + *used_entries = used_entry_count; + } + return err; +} diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index 11dcb17d1..6b8d1fdac 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -862,4 +862,33 @@ void Page::debugDump() const } } +esp_err_t Page::calcEntries(nvs_stats_t &nvsStats) +{ + assert(mState != PageState::FREEING); + + nvsStats.total_entries += ENTRY_COUNT; + + switch (mState) { + case PageState::UNINITIALIZED: + case PageState::CORRUPT: + nvsStats.free_entries += ENTRY_COUNT; + break; + + case PageState::FULL: + case PageState::ACTIVE: + nvsStats.used_entries += mUsedEntryCount; + nvsStats.free_entries += ENTRY_COUNT - mUsedEntryCount; // it's equivalent free + erase entries. + break; + + case PageState::INVALID: + return ESP_ERR_INVALID_STATE; + break; + + default: + assert(false && "Unhandled state"); + break; + } + return ESP_OK; +} + } // namespace nvs diff --git a/components/nvs_flash/src/nvs_page.hpp b/components/nvs_flash/src/nvs_page.hpp index 7731e403a..7aa8b9fd8 100644 --- a/components/nvs_flash/src/nvs_page.hpp +++ b/components/nvs_flash/src/nvs_page.hpp @@ -133,6 +133,8 @@ public: void debugDump() const; + esp_err_t calcEntries(nvs_stats_t &nvsStats); + protected: class Header diff --git a/components/nvs_flash/src/nvs_pagemanager.cpp b/components/nvs_flash/src/nvs_pagemanager.cpp index 943f54f2f..314593525 100644 --- a/components/nvs_flash/src/nvs_pagemanager.cpp +++ b/components/nvs_flash/src/nvs_pagemanager.cpp @@ -197,4 +197,26 @@ esp_err_t PageManager::activatePage() return ESP_OK; } +esp_err_t PageManager::fillStats(nvs_stats_t& nvsStats) +{ + nvsStats.used_entries = 0; + nvsStats.free_entries = 0; + nvsStats.total_entries = 0; + esp_err_t err = ESP_OK; + + // list of used pages + for (auto p = mPageList.begin(); p != mPageList.end(); ++p) { + err = p->calcEntries(nvsStats); + if (err != ESP_OK) { + return err; + } + } + + // free pages + nvsStats.total_entries += mFreePageList.size() * Page::ENTRY_COUNT; + nvsStats.free_entries += mFreePageList.size() * Page::ENTRY_COUNT; + + return err; +} + } // namespace nvs diff --git a/components/nvs_flash/src/nvs_pagemanager.hpp b/components/nvs_flash/src/nvs_pagemanager.hpp index 10c545f0f..74305e952 100644 --- a/components/nvs_flash/src/nvs_pagemanager.hpp +++ b/components/nvs_flash/src/nvs_pagemanager.hpp @@ -50,6 +50,8 @@ public: esp_err_t requestNewPage(); + esp_err_t fillStats(nvs_stats_t& nvsStats); + protected: friend class Iterator; diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index f8da28fa2..eaf48cc19 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -291,4 +291,37 @@ void Storage::debugCheck() } #endif //ESP_PLATFORM +esp_err_t Storage::fillStats(nvs_stats_t& nvsStats) +{ + nvsStats.namespace_count = mNamespaces.size(); + return mPageManager.fillStats(nvsStats); +} + +esp_err_t Storage::calcEntriesInNamespace(uint8_t nsIndex, size_t& usedEntries) +{ + usedEntries = 0; + + if (mState != StorageState::ACTIVE) { + return ESP_ERR_NVS_NOT_INITIALIZED; + } + + for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { + size_t itemIndex = 0; + Item item; + while (true) { + auto err = it->findItem(nsIndex, ItemType::ANY, nullptr, itemIndex, item); + if (err == ESP_ERR_NVS_NOT_FOUND) { + break; + } + else if (err != ESP_OK) { + return err; + } + usedEntries += item.span; + itemIndex += item.span; + if(itemIndex >= it->ENTRY_COUNT) break; + } + } + return ESP_OK; +} + } diff --git a/components/nvs_flash/src/nvs_storage.hpp b/components/nvs_flash/src/nvs_storage.hpp index 3c0e0c85a..18ec8ecd8 100644 --- a/components/nvs_flash/src/nvs_storage.hpp +++ b/components/nvs_flash/src/nvs_storage.hpp @@ -89,6 +89,9 @@ public: void debugCheck(); + esp_err_t fillStats(nvs_stats_t& nvsStats); + + esp_err_t calcEntriesInNamespace(uint8_t nsIndex, size_t& usedEntries); protected: diff --git a/components/nvs_flash/test/test_nvs.c b/components/nvs_flash/test/test_nvs.c index ed0884a7d..294c3f644 100644 --- a/components/nvs_flash/test/test_nvs.c +++ b/components/nvs_flash/test/test_nvs.c @@ -65,3 +65,151 @@ TEST_CASE("various nvs tests", "[nvs]") nvs_close(handle_2); } + +TEST_CASE("calculate used and free space", "[nvs]") +{ + TEST_ESP_ERR(nvs_get_stats(NULL, NULL), ESP_ERR_INVALID_ARG); + nvs_stats_t stat1; + nvs_stats_t stat2; + TEST_ESP_ERR(nvs_get_stats(NULL, &stat1), ESP_ERR_NVS_PART_NOT_FOUND); + TEST_ASSERT_TRUE(stat1.free_entries == 0); + TEST_ASSERT_TRUE(stat1.namespace_count == 0); + TEST_ASSERT_TRUE(stat1.total_entries == 0); + TEST_ASSERT_TRUE(stat1.used_entries == 0); + + nvs_handle handle = 0; + size_t h_count_entries; + TEST_ESP_ERR(nvs_get_used_entry_count(handle, &h_count_entries), ESP_ERR_NVS_INVALID_HANDLE); + TEST_ASSERT_TRUE(h_count_entries == 0); + + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_LOGW(TAG, "nvs_flash_init failed (0x%x), erasing partition and retrying", err); + const esp_partition_t* nvs_partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); + assert(nvs_partition && "partition table must have an NVS partition"); + ESP_ERROR_CHECK( esp_partition_erase_range(nvs_partition, 0, nvs_partition->size) ); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK( err ); + + // erase if have any namespace + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + if(stat1.namespace_count != 0) { + TEST_ESP_OK(nvs_flash_erase()); + TEST_ESP_OK(nvs_flash_deinit()); + TEST_ESP_OK(nvs_flash_init()); + } + + // after erase. empty partition + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + TEST_ASSERT_TRUE(stat1.free_entries != 0); + TEST_ASSERT_TRUE(stat1.namespace_count == 0); + TEST_ASSERT_TRUE(stat1.total_entries != 0); + TEST_ASSERT_TRUE(stat1.used_entries == 0); + + // create namespace test_k1 + nvs_handle handle_1; + TEST_ESP_OK(nvs_open("test_k1", NVS_READWRITE, &handle_1)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat2)); + TEST_ASSERT_TRUE(stat2.free_entries + 1 == stat1.free_entries); + TEST_ASSERT_TRUE(stat2.namespace_count == 1); + TEST_ASSERT_TRUE(stat2.total_entries == stat1.total_entries); + TEST_ASSERT_TRUE(stat2.used_entries == 1); + + // create pair key-value com + TEST_ESP_OK(nvs_set_i32(handle_1, "com", 0x12345678)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + TEST_ASSERT_TRUE(stat1.free_entries + 1 == stat2.free_entries); + TEST_ASSERT_TRUE(stat1.namespace_count == 1); + TEST_ASSERT_TRUE(stat1.total_entries == stat2.total_entries); + TEST_ASSERT_TRUE(stat1.used_entries == 2); + + // change value in com + TEST_ESP_OK(nvs_set_i32(handle_1, "com", 0x01234567)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat2)); + TEST_ASSERT_TRUE(stat2.free_entries == stat1.free_entries); + TEST_ASSERT_TRUE(stat2.namespace_count == 1); + TEST_ASSERT_TRUE(stat2.total_entries != 0); + TEST_ASSERT_TRUE(stat2.used_entries == 2); + + // create pair key-value ru + TEST_ESP_OK(nvs_set_i32(handle_1, "ru", 0x00FF00FF)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + TEST_ASSERT_TRUE(stat1.free_entries + 1 == stat2.free_entries); + TEST_ASSERT_TRUE(stat1.namespace_count == 1); + TEST_ASSERT_TRUE(stat1.total_entries != 0); + TEST_ASSERT_TRUE(stat1.used_entries == 3); + + // amount valid pair in namespace 1 + size_t h1_count_entries; + TEST_ESP_OK(nvs_get_used_entry_count(handle_1, &h1_count_entries)); + TEST_ASSERT_TRUE(h1_count_entries == 2); + + nvs_handle handle_2; + // create namespace test_k2 + TEST_ESP_OK(nvs_open("test_k2", NVS_READWRITE, &handle_2)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat2)); + TEST_ASSERT_TRUE(stat2.free_entries + 1 == stat1.free_entries); + TEST_ASSERT_TRUE(stat2.namespace_count == 2); + TEST_ASSERT_TRUE(stat2.total_entries == stat1.total_entries); + TEST_ASSERT_TRUE(stat2.used_entries == 4); + + // create pair key-value + TEST_ESP_OK(nvs_set_i32(handle_2, "su1", 0x00000001)); + TEST_ESP_OK(nvs_set_i32(handle_2, "su2", 0x00000002)); + TEST_ESP_OK(nvs_set_i32(handle_2, "sus", 0x00000003)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + TEST_ASSERT_TRUE(stat1.free_entries + 3 == stat2.free_entries); + TEST_ASSERT_TRUE(stat1.namespace_count == 2); + TEST_ASSERT_TRUE(stat1.total_entries == stat2.total_entries); + TEST_ASSERT_TRUE(stat1.used_entries == 7); + + TEST_ASSERT_TRUE(stat1.total_entries == (stat1.used_entries + stat1.free_entries)); + + // amount valid pair in namespace 2 + size_t h2_count_entries; + TEST_ESP_OK(nvs_get_used_entry_count(handle_2, &h2_count_entries)); + TEST_ASSERT_TRUE(h2_count_entries == 3); + + TEST_ASSERT_TRUE(stat1.used_entries == (h1_count_entries + h2_count_entries + stat1.namespace_count)); + + nvs_close(handle_1); + nvs_close(handle_2); + + size_t temp = h2_count_entries; + TEST_ESP_ERR(nvs_get_used_entry_count(handle_1, &h2_count_entries), ESP_ERR_NVS_INVALID_HANDLE); + TEST_ASSERT_TRUE(h2_count_entries == 0); + h2_count_entries = temp; + TEST_ESP_ERR(nvs_get_used_entry_count(handle_1, NULL), ESP_ERR_INVALID_ARG); + + nvs_handle handle_3; + // create namespace test_k3 + TEST_ESP_OK(nvs_open("test_k3", NVS_READWRITE, &handle_3)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat2)); + TEST_ASSERT_TRUE(stat2.free_entries + 1 == stat1.free_entries); + TEST_ASSERT_TRUE(stat2.namespace_count == 3); + TEST_ASSERT_TRUE(stat2.total_entries == stat1.total_entries); + TEST_ASSERT_TRUE(stat2.used_entries == 8); + + // create pair blobs + uint32_t blob[12]; + TEST_ESP_OK(nvs_set_blob(handle_3, "bl1", &blob, sizeof(blob))); + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + TEST_ASSERT_TRUE(stat1.free_entries + 3 == stat2.free_entries); + TEST_ASSERT_TRUE(stat1.namespace_count == 3); + TEST_ASSERT_TRUE(stat1.total_entries == stat2.total_entries); + TEST_ASSERT_TRUE(stat1.used_entries == 11); + + // amount valid pair in namespace 2 + size_t h3_count_entries; + TEST_ESP_OK(nvs_get_used_entry_count(handle_3, &h3_count_entries)); + TEST_ASSERT_TRUE(h3_count_entries == 3); + + TEST_ASSERT_TRUE(stat1.used_entries == (h1_count_entries + h2_count_entries + h3_count_entries + stat1.namespace_count)); + + nvs_close(handle_3); + + TEST_ESP_OK(nvs_flash_erase()); + TEST_ESP_OK(nvs_flash_deinit()); +} diff --git a/components/nvs_flash/test_nvs_host/test_nvs.cpp b/components/nvs_flash/test_nvs_host/test_nvs.cpp index f419256cf..7e2c1e02b 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs.cpp @@ -132,14 +132,14 @@ TEST_CASE("when writing and erasing, used/erased counts are updated correctly", CHECK(page.getErasedEntryCount() == 1); for (size_t i = 0; i < Page::ENTRY_COUNT - 2; ++i) { char name[16]; - snprintf(name, sizeof(name), "i%ld", i); + snprintf(name, sizeof(name), "i%ld", (long int)i); CHECK(page.writeItem(1, name, i) == ESP_OK); } CHECK(page.getUsedEntryCount() == Page::ENTRY_COUNT - 1); CHECK(page.getErasedEntryCount() == 1); for (size_t i = 0; i < Page::ENTRY_COUNT - 2; ++i) { char name[16]; - snprintf(name, sizeof(name), "i%ld", i); + snprintf(name, sizeof(name), "i%ld", (long int)i); CHECK(page.eraseItem(1, itemTypeOf(), name) == ESP_OK); } CHECK(page.getUsedEntryCount() == 1); @@ -153,7 +153,7 @@ TEST_CASE("when page is full, adding an element fails", "[nvs]") CHECK(page.load(0) == ESP_OK); for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) { char name[16]; - snprintf(name, sizeof(name), "i%ld", i); + snprintf(name, sizeof(name), "i%ld", (long int)i); CHECK(page.writeItem(1, name, i) == ESP_OK); } CHECK(page.writeItem(1, "foo", 64UL) == ESP_ERR_NVS_PAGE_FULL); @@ -1123,7 +1123,7 @@ TEST_CASE("crc errors in item header are handled", "[nvs]") // add more items to make the page full for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) { char item_name[Item::MAX_KEY_LENGTH + 1]; - snprintf(item_name, sizeof(item_name), "item_%ld", i); + snprintf(item_name, sizeof(item_name), "item_%ld", (long int)i); TEST_ESP_OK(storage.writeItem(1, item_name, static_cast(i))); } @@ -1252,3 +1252,137 @@ TEST_CASE("dump all performance data", "[nvs]") std::cout << s_perf.str() << std::endl; std::cout << "====================" << std::endl; } + +TEST_CASE("calculate used and free space", "[nvs]") +{ + SpiFlashEmulator emu(6); + TEST_ESP_ERR(nvs_get_stats(NULL, NULL), ESP_ERR_INVALID_ARG); + nvs_stats_t stat1; + nvs_stats_t stat2; + TEST_ESP_ERR(nvs_get_stats(NULL, &stat1), ESP_ERR_NVS_NOT_INITIALIZED); + CHECK(stat1.free_entries == 0); + CHECK(stat1.namespace_count == 0); + CHECK(stat1.total_entries == 0); + CHECK(stat1.used_entries == 0); + + nvs_handle handle = 0; + size_t h_count_entries; + TEST_ESP_ERR(nvs_get_used_entry_count(handle, &h_count_entries), ESP_ERR_NVS_INVALID_HANDLE); + CHECK(h_count_entries == 0); + + // init nvs + TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 6)); + + TEST_ESP_ERR(nvs_get_used_entry_count(handle, &h_count_entries), ESP_ERR_NVS_INVALID_HANDLE); + CHECK(h_count_entries == 0); + + Page p; + // after erase. empty partition + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + CHECK(stat1.free_entries != 0); + CHECK(stat1.namespace_count == 0); + CHECK(stat1.total_entries == 6 * p.ENTRY_COUNT); + CHECK(stat1.used_entries == 0); + + // create namespace test_k1 + nvs_handle handle_1; + TEST_ESP_OK(nvs_open("test_k1", NVS_READWRITE, &handle_1)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat2)); + CHECK(stat2.free_entries + 1 == stat1.free_entries); + CHECK(stat2.namespace_count == 1); + CHECK(stat2.total_entries == stat1.total_entries); + CHECK(stat2.used_entries == 1); + + // create pair key-value com + TEST_ESP_OK(nvs_set_i32(handle_1, "com", 0x12345678)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + CHECK(stat1.free_entries + 1 == stat2.free_entries); + CHECK(stat1.namespace_count == 1); + CHECK(stat1.total_entries == stat2.total_entries); + CHECK(stat1.used_entries == 2); + + // change value in com + TEST_ESP_OK(nvs_set_i32(handle_1, "com", 0x01234567)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat2)); + CHECK(stat2.free_entries == stat1.free_entries); + CHECK(stat2.namespace_count == 1); + CHECK(stat2.total_entries != 0); + CHECK(stat2.used_entries == 2); + + // create pair key-value ru + TEST_ESP_OK(nvs_set_i32(handle_1, "ru", 0x00FF00FF)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + CHECK(stat1.free_entries + 1 == stat2.free_entries); + CHECK(stat1.namespace_count == 1); + CHECK(stat1.total_entries != 0); + CHECK(stat1.used_entries == 3); + + // amount valid pair in namespace 1 + size_t h1_count_entries; + TEST_ESP_OK(nvs_get_used_entry_count(handle_1, &h1_count_entries)); + CHECK(h1_count_entries == 2); + + nvs_handle handle_2; + // create namespace test_k2 + TEST_ESP_OK(nvs_open("test_k2", NVS_READWRITE, &handle_2)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat2)); + CHECK(stat2.free_entries + 1 == stat1.free_entries); + CHECK(stat2.namespace_count == 2); + CHECK(stat2.total_entries == stat1.total_entries); + CHECK(stat2.used_entries == 4); + + // create pair key-value + TEST_ESP_OK(nvs_set_i32(handle_2, "su1", 0x00000001)); + TEST_ESP_OK(nvs_set_i32(handle_2, "su2", 0x00000002)); + TEST_ESP_OK(nvs_set_i32(handle_2, "sus", 0x00000003)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + CHECK(stat1.free_entries + 3 == stat2.free_entries); + CHECK(stat1.namespace_count == 2); + CHECK(stat1.total_entries == stat2.total_entries); + CHECK(stat1.used_entries == 7); + + CHECK(stat1.total_entries == (stat1.used_entries + stat1.free_entries)); + + // amount valid pair in namespace 2 + size_t h2_count_entries; + TEST_ESP_OK(nvs_get_used_entry_count(handle_2, &h2_count_entries)); + CHECK(h2_count_entries == 3); + + CHECK(stat1.used_entries == (h1_count_entries + h2_count_entries + stat1.namespace_count)); + + nvs_close(handle_1); + nvs_close(handle_2); + + size_t temp = h2_count_entries; + TEST_ESP_ERR(nvs_get_used_entry_count(handle_1, &h2_count_entries), ESP_ERR_NVS_INVALID_HANDLE); + CHECK(h2_count_entries == 0); + h2_count_entries = temp; + TEST_ESP_ERR(nvs_get_used_entry_count(handle_1, NULL), ESP_ERR_INVALID_ARG); + + nvs_handle handle_3; + // create namespace test_k3 + TEST_ESP_OK(nvs_open("test_k3", NVS_READWRITE, &handle_3)); + TEST_ESP_OK(nvs_get_stats(NULL, &stat2)); + CHECK(stat2.free_entries + 1 == stat1.free_entries); + CHECK(stat2.namespace_count == 3); + CHECK(stat2.total_entries == stat1.total_entries); + CHECK(stat2.used_entries == 8); + + // create pair blobs + uint32_t blob[12]; + TEST_ESP_OK(nvs_set_blob(handle_3, "bl1", &blob, sizeof(blob))); + TEST_ESP_OK(nvs_get_stats(NULL, &stat1)); + CHECK(stat1.free_entries + 3 == stat2.free_entries); + CHECK(stat1.namespace_count == 3); + CHECK(stat1.total_entries == stat2.total_entries); + CHECK(stat1.used_entries == 11); + + // amount valid pair in namespace 2 + size_t h3_count_entries; + TEST_ESP_OK(nvs_get_used_entry_count(handle_3, &h3_count_entries)); + CHECK(h3_count_entries == 3); + + CHECK(stat1.used_entries == (h1_count_entries + h2_count_entries + h3_count_entries + stat1.namespace_count)); + + nvs_close(handle_3); +}