]> granicus.if.org Git - esp-idf/commitdiff
nvs:Add functions for calculating used/free entries
authorkonstantin <konstantin@espressif.com>
Tue, 20 Feb 2018 07:11:56 +0000 (12:11 +0500)
committerKonstantin Kondrashov <konstantin@espressif.com>
Fri, 13 Apr 2018 05:34:13 +0000 (10:34 +0500)
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
components/nvs_flash/src/nvs_api.cpp
components/nvs_flash/src/nvs_page.cpp
components/nvs_flash/src/nvs_page.hpp
components/nvs_flash/src/nvs_pagemanager.cpp
components/nvs_flash/src/nvs_pagemanager.hpp
components/nvs_flash/src/nvs_storage.cpp
components/nvs_flash/src/nvs_storage.hpp
components/nvs_flash/test/test_nvs.c
components/nvs_flash/test_nvs_host/test_nvs.cpp

index dfdd18c92c889586f2e56fb586ab56e568faa151..c58f62f9c6455bad6460a20c7f516d529f34d44a 100644 (file)
@@ -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"
index 15f76d1dd2908f1e45e9b59272b36a1f79389567..cb23a4fc901b798489a7ee466d112bc2c7150a49 100644 (file)
@@ -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;
+}
index 11dcb17d1d373c7a127638575d035c1d37892efb..6b8d1fdacba20fa478c9907f7ee63e8519632279 100644 (file)
@@ -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
index 7731e403aedbab981e90840a6311dc50d2c633c7..7aa8b9fd831633480668e87681c229fb29bd6b4f 100644 (file)
@@ -133,6 +133,8 @@ public:
 
     void debugDump() const;
 
+    esp_err_t calcEntries(nvs_stats_t &nvsStats);
+
 protected:
 
     class Header
index 943f54f2f8125f9892023bd0db4633c501da9682..31459352587a3193fa42269cc13241444592e478 100644 (file)
@@ -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
index 10c545f0ffc67342a40dad85a8fd9e943d5fc506..74305e95200c1a91987db963f73fe8ddf6688ac7 100644 (file)
@@ -50,6 +50,8 @@ public:
 
     esp_err_t requestNewPage();
 
+    esp_err_t fillStats(nvs_stats_t& nvsStats);
+
 protected:
     friend class Iterator;
 
index f8da28fa242b34c99927f46cacba67168799a0a3..eaf48cc199032830984b2ace5d8dddd4c8f3ff47 100644 (file)
@@ -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;
+}
+
 }
index 3c0e0c85a5beca8a1d232b945a1ef00eb0644fd7..18ec8ecd8dbaa2c50f01563cff12a8e5ce9152c7 100644 (file)
@@ -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:
 
index ed0884a7d8661b15a1ce2e20961c0b372b277de8..294c3f6449902fac170e3d736d5aa0b37b8daf5f 100644 (file)
@@ -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());
+}
index f419256cf334b9b8c7db3cd0f025c96e7cd53bce..7e2c1e02be7ee84c9640546d7dbf296034ab9db9 100644 (file)
@@ -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<size_t>(), 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<uint32_t>(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);
+}