diff --git a/.gitlab/ci/host-test.yml b/.gitlab/ci/host-test.yml index 160719eb81..d33564b05a 100644 --- a/.gitlab/ci/host-test.yml +++ b/.gitlab/ci/host-test.yml @@ -20,6 +20,13 @@ test_nvs_on_host: - cd components/nvs_flash/test_nvs_host - make test +test_nvs_on_host_cmake: + extends: .host_test_template + script: + - cd ${IDF_PATH}/components/nvs_flash/host_test/nvs_host_test + - idf.py build + - build/nvs_host_test.elf + test_nvs_coverage: extends: - .host_test_template diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index f19655a71d..a26493ce3d 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -17,6 +17,7 @@ idf_component_register(SRCS "${srcs}" REQUIRES "esp_partition" PRIV_REQUIRES spi_flash INCLUDE_DIRS "include" + "../spi_flash/include" PRIV_INCLUDE_DIRS "private_include") # If we use the linux target, we need to redirect the crc functions to the linux @@ -28,6 +29,13 @@ if(${target} STREQUAL "linux") target_compile_options(${COMPONENT_LIB} PUBLIC "-DLINUX_TARGET") target_compile_options(${COMPONENT_LIB} PUBLIC --coverage) target_link_libraries(${COMPONENT_LIB} PUBLIC --coverage) + + find_library(LIB_BSD bsd) + if(LIB_BSD) + target_link_libraries(${COMPONENT_LIB} PRIVATE ${LIB_BSD}) + elseif(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + message(WARNING "Missing LIBBSD library. Install libbsd-dev package and/or check linker directories.") + endif() endif() if(CONFIG_NVS_ENCRYPTION) diff --git a/components/nvs_flash/host_test/fixtures/test_fixtures.hpp b/components/nvs_flash/host_test/fixtures/test_fixtures.hpp index b0915682f1..fdf2c110e3 100644 --- a/components/nvs_flash/host_test/fixtures/test_fixtures.hpp +++ b/components/nvs_flash/host_test/fixtures/test_fixtures.hpp @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// 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. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "nvs_partition.hpp" #include "nvs.h" @@ -29,8 +21,7 @@ extern "C" { struct FixtureException : std::exception { FixtureException(const std::string& msg) : msg(msg) { } - - const char *what() { + const char *what() const noexcept { return msg.c_str(); } diff --git a/components/nvs_flash/host_test/nvs_host_test/CMakeLists.txt b/components/nvs_flash/host_test/nvs_host_test/CMakeLists.txt new file mode 100644 index 0000000000..655d83dcf0 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) +# Freertos is included via common components. However, CATCH isn't compatible with the FreeRTOS component yet, hence +# using the FreeRTOS mock component. +# target. +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/") + +project(nvs_host_test) + +add_dependencies(nvs_host_test.elf partition-table) diff --git a/components/nvs_flash/host_test/nvs_host_test/README.md b/components/nvs_flash/host_test/nvs_host_test/README.md new file mode 100644 index 0000000000..37c142df16 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/README.md @@ -0,0 +1,2 @@ +| Supported Targets | Linux | +| ----------------- | ----- | diff --git a/components/nvs_flash/host_test/nvs_host_test/main/CMakeLists.txt b/components/nvs_flash/host_test/nvs_host_test/main/CMakeLists.txt new file mode 100644 index 0000000000..05663cb008 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/main/CMakeLists.txt @@ -0,0 +1,14 @@ +idf_component_register(SRCS "main.cpp" + "test_nvs.cpp" + "test_partition_manager.cpp" + "test_nvs_cxx_api.cpp" + "test_nvs_handle.cpp" + "test_nvs_initialization.cpp" + "test_nvs_storage.cpp" + INCLUDE_DIRS + "../../../src" + "../../../private_include" + "../../../../mbedtls/mbedtls/include" + "../../../../../tools/catch" + WHOLE_ARCHIVE + REQUIRES nvs_flash) diff --git a/components/nvs_flash/host_test/nvs_host_test/main/main.cpp b/components/nvs_flash/host_test/nvs_host_test/main/main.cpp new file mode 100644 index 0000000000..dddf85b1d5 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/main/main.cpp @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/components/nvs_flash/host_test/nvs_host_test/main/test_fixtures.hpp b/components/nvs_flash/host_test/nvs_host_test/main/test_fixtures.hpp new file mode 100644 index 0000000000..f98e136743 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_fixtures.hpp @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "nvs_partition.hpp" +#include "esp_private/partition_linux.h" +#include "nvs.h" + +class PartitionEmulationFixture { +public: + PartitionEmulationFixture( uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME) : + esp_partition() + { + + if (esp_partition_file_mmap((const uint8_t **) &p_part_desc_addr_start) != ESP_OK) { + throw ("Failed to initialize esp_partition_file_mmap"); + } + + esp_partition.address = start_sector * SPI_FLASH_SEC_SIZE; + esp_partition.size = sector_size * SPI_FLASH_SEC_SIZE; + esp_partition.erase_size = ESP_PARTITION_EMULATED_SECTOR_SIZE; + strncpy(esp_partition.label, partition_name, PART_NAME_MAX_SIZE); + p_part = new nvs::NVSPartition(&esp_partition); + } + + ~PartitionEmulationFixture() + { + delete p_part; + esp_partition_file_munmap(); + } + + nvs::NVSPartition *part() + { + return p_part; + } + + nvs::NVSPartition *p_part; + esp_partition_t esp_partition; + uint8_t *p_part_desc_addr_start; +}; diff --git a/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp new file mode 100644 index 0000000000..48f07285bc --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp @@ -0,0 +1,2057 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "catch.hpp" +#include "nvs.hpp" +#include "sdkconfig.h" +#include "nvs_partition_manager.hpp" +#include "nvs_partition.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "test_fixtures.hpp" + +#define TEST_ESP_ERR(rc, res) CHECK((rc) == (res)) +#define TEST_ESP_OK(rc) CHECK((rc) == ESP_OK) + +#define TEMPORARILY_DISABLED(x) + +stringstream s_perf; + +TEST_CASE("crc32 behaves as expected", "[nvs]") +{ + nvs::Item item1; + item1.datatype = nvs::ItemType::I32; + item1.nsIndex = 1; + item1.crc32 = 0; + item1.chunkIndex = 0xff; + fill_n(item1.key, sizeof(item1.key), 0xbb); + fill_n(item1.data, sizeof(item1.data), 0xaa); + + auto crc32_1 = item1.calculateCrc32(); + + nvs::Item item2 = item1; + item2.crc32 = crc32_1; + + CHECK(crc32_1 == item2.calculateCrc32()); + + item2 = item1; + item2.nsIndex = 2; + CHECK(crc32_1 != item2.calculateCrc32()); + + item2 = item1; + item2.datatype = nvs::ItemType::U32; + CHECK(crc32_1 != item2.calculateCrc32()); + + item2 = item1; + strncpy(item2.key, "foo", nvs::Item::MAX_KEY_LENGTH); + CHECK(crc32_1 != item2.calculateCrc32()); +} + +TEST_CASE("Page starting with empty flash is in uninitialized state", "[nvs]") +{ + PartitionEmulationFixture f; + nvs::Page page; + CHECK(page.state() == nvs::Page::PageState::INVALID); + CHECK(page.load(f.part(), 0) == ESP_OK); + CHECK(page.state() == nvs::Page::PageState::UNINITIALIZED); +} + +TEST_CASE("Page can distinguish namespaces", "[nvs]") +{ + PartitionEmulationFixture f; + nvs::Page page; + CHECK(page.load(f.part(), 0) == ESP_OK); + int32_t val1 = 0x12345678; + CHECK(page.writeItem(1, nvs::ItemType::I32, "intval1", &val1, sizeof(val1)) == ESP_OK); + int32_t val2 = 0x23456789; + CHECK(page.writeItem(2, nvs::ItemType::I32, "intval1", &val2, sizeof(val2)) == ESP_OK); + + int32_t readVal; + CHECK(page.readItem(2, nvs::ItemType::I32, "intval1", &readVal, sizeof(readVal)) == ESP_OK); + CHECK(readVal == val2); +} + +TEST_CASE("Page reading with different type causes type mismatch error", "[nvs]") +{ + PartitionEmulationFixture f; + nvs::Page page; + CHECK(page.load(f.part(), 0) == ESP_OK); + int32_t val = 0x12345678; + CHECK(page.writeItem(1, nvs::ItemType::I32, "intval1", &val, sizeof(val)) == ESP_OK); + CHECK(page.readItem(1, nvs::ItemType::U32, "intval1", &val, sizeof(val)) == ESP_ERR_NVS_TYPE_MISMATCH); +} + +TEST_CASE("Page when erased, it's state becomes UNITIALIZED", "[nvs]") +{ + PartitionEmulationFixture f; + nvs::Page page; + CHECK(page.load(f.part(), 0) == ESP_OK); + int32_t val = 0x12345678; + CHECK(page.writeItem(1, nvs::ItemType::I32, "intval1", &val, sizeof(val)) == ESP_OK); + CHECK(page.erase() == ESP_OK); + CHECK(page.state() == nvs::Page::PageState::UNINITIALIZED); +} + +TEST_CASE("Page when writing and erasing, used/erased counts are updated correctly", "[nvs]") +{ + PartitionEmulationFixture f; + nvs::Page page; + CHECK(page.load(f.part(), 0) == ESP_OK); + CHECK(page.getUsedEntryCount() == 0); + CHECK(page.getErasedEntryCount() == 0); + uint32_t foo1 = 0; + CHECK(page.writeItem(1, "foo1", foo1) == ESP_OK); + CHECK(page.getUsedEntryCount() == 1); + CHECK(page.writeItem(2, "foo1", foo1) == ESP_OK); + CHECK(page.getUsedEntryCount() == 2); + CHECK(page.eraseItem(2, "foo1") == ESP_OK); + CHECK(page.getUsedEntryCount() == 1); + CHECK(page.getErasedEntryCount() == 1); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT - 2; ++i) { + char name[16]; + snprintf(name, sizeof(name), "i%ld", (long int)i); + CHECK(page.writeItem(1, name, i) == ESP_OK); + } + CHECK(page.getUsedEntryCount() == nvs::Page::ENTRY_COUNT - 1); + CHECK(page.getErasedEntryCount() == 1); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT - 2; ++i) { + char name[16]; + snprintf(name, sizeof(name), "i%ld", (long int)i); + CHECK(page.eraseItem(1, nvs::itemTypeOf(), name) == ESP_OK); + } + CHECK(page.getUsedEntryCount() == 1); + CHECK(page.getErasedEntryCount() == nvs::Page::ENTRY_COUNT - 1); +} + +TEST_CASE("Page when page is full, adding an element fails", "[nvs]") +{ + PartitionEmulationFixture f; + nvs::Page page; + CHECK(page.load(f.part(), 0) == ESP_OK); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT; ++i) { + char name[16]; + 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); +} + +TEST_CASE("Page maintains its seq number") +{ + PartitionEmulationFixture f; + { + nvs::Page page; + CHECK(page.load(f.part(), 0) == ESP_OK); + CHECK(page.setSeqNumber(123) == ESP_OK); + int32_t val = 42; + CHECK(page.writeItem(1, nvs::ItemType::I32, "dummy", &val, sizeof(val)) == ESP_OK); + } + { + nvs::Page page; + CHECK(page.load(f.part(), 0) == ESP_OK); + uint32_t seqno; + CHECK(page.getSeqNumber(seqno) == ESP_OK); + CHECK(seqno == 123); + } +} + +TEST_CASE("Page can write and read variable length data", "[nvs]") +{ + PartitionEmulationFixture f; + nvs::Page page; + CHECK(page.load(f.part(), 0) == ESP_OK); + const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; + size_t len = strlen(str); + CHECK(page.writeItem(1, "stuff1", 42) == ESP_OK); + CHECK(page.writeItem(1, "stuff2", 1) == ESP_OK); + CHECK(page.writeItem(1, nvs::ItemType::SZ, "foobaar", str, len + 1) == ESP_OK); + CHECK(page.writeItem(1, "stuff3", 2) == ESP_OK); + CHECK(page.writeItem(1, nvs::ItemType::BLOB, "baz", str, len) == ESP_OK); + CHECK(page.writeItem(1, "stuff4", 0x7abbccdd) == ESP_OK); + + char buf[sizeof(str) + 16]; + int32_t value; + CHECK(page.readItem(1, "stuff1", value) == ESP_OK); + CHECK(value == 42); + CHECK(page.readItem(1, "stuff2", value) == ESP_OK); + CHECK(value == 1); + CHECK(page.readItem(1, "stuff3", value) == ESP_OK); + CHECK(value == 2); + CHECK(page.readItem(1, "stuff4", value) == ESP_OK); + CHECK(value == 0x7abbccdd); + + fill_n(buf, sizeof(buf), 0xff); + CHECK(page.readItem(1, nvs::ItemType::SZ, "foobaar", buf, sizeof(buf)) == ESP_OK); + CHECK(memcmp(buf, str, strlen(str) + 1) == 0); + + fill_n(buf, sizeof(buf), 0xff); + CHECK(page.readItem(1, nvs::ItemType::BLOB, "baz", buf, sizeof(buf)) == ESP_OK); + CHECK(memcmp(buf, str, strlen(str)) == 0); +} + +TEST_CASE("Page different key names are distinguished even if the pointer is the same", "[nvs]") +{ + PartitionEmulationFixture f; + nvs::Page page; + TEST_ESP_OK(page.load(f.part(), 0)); + TEST_ESP_OK(page.writeItem(1, "i1", 1)); + TEST_ESP_OK(page.writeItem(1, "i2", 2)); + int32_t value; + char keyname[10] = {0}; + for (int i = 0; i < 2; ++i) { + strncpy(keyname, "i1", sizeof(keyname) - 1); + TEST_ESP_OK(page.readItem(1, keyname, value)); + CHECK(value == 1); + strncpy(keyname, "i2", sizeof(keyname) - 1); + TEST_ESP_OK(page.readItem(1, keyname, value)); + CHECK(value == 2); + } +} + +TEST_CASE("Page validates key size", "[nvs]") +{ + PartitionEmulationFixture f(0, 4); + nvs::Page page; + TEST_ESP_OK(page.load(f.part(), 0)); + // 16-character key fails + TEST_ESP_ERR(page.writeItem(1, "0123456789123456", 1), ESP_ERR_NVS_KEY_TOO_LONG); + // 15-character key is okay + TEST_ESP_OK(page.writeItem(1, "012345678912345", 1)); +} + +TEST_CASE("Page validates blob size", "[nvs]") +{ + PartitionEmulationFixture f(0, 4); + nvs::Page page; + TEST_ESP_OK(page.load(f.part(), 0)); + + char buf[4096] = { 0 }; + // There are two potential errors here: + // - not enough space in the page (because one value has been written already) + // - value is too long + // Check that the second one is actually returned. + TEST_ESP_ERR(page.writeItem(1, nvs::ItemType::BLOB, "2", buf, nvs::Page::ENTRY_COUNT * nvs::Page::ENTRY_SIZE), ESP_ERR_NVS_VALUE_TOO_LONG); + // Should fail as well + TEST_ESP_ERR(page.writeItem(1, nvs::ItemType::BLOB, "2", buf, nvs::Page::CHUNK_MAX_SIZE + 1), ESP_ERR_NVS_VALUE_TOO_LONG); + TEST_ESP_OK(page.writeItem(1, nvs::ItemType::BLOB, "2", buf, nvs::Page::CHUNK_MAX_SIZE)); +} + +class HashListTestHelper : public nvs::HashList { +public: + size_t getBlockCount() + { + return mBlockList.size(); + } +}; + +TEST_CASE("HashList is cleaned up as soon as items are erased", "[nvs]") +{ + HashListTestHelper hashlist; + // Add items + const size_t count = 128; + for (size_t i = 0; i < count; ++i) { + char key[16]; + snprintf(key, sizeof(key), "i%ld", (long int)i); + nvs::Item item(1, nvs::ItemType::U32, 1, key); + hashlist.insert(item, i); + } + INFO("Added " << count << " items, " << hashlist.getBlockCount() << " blocks"); + // Remove them in reverse order + for (size_t i = count; i > 0; --i) { + // Make sure that the element existed before it's erased + CHECK(hashlist.erase(i - 1) == true); + } + CHECK(hashlist.getBlockCount() == 0); + // Add again + for (size_t i = 0; i < count; ++i) { + char key[16]; + snprintf(key, sizeof(key), "i%ld", (long int)i); + nvs::Item item(1, nvs::ItemType::U32, 1, key); + hashlist.insert(item, i); + } + INFO("Added " << count << " items, " << hashlist.getBlockCount() << " blocks"); + // Remove them in the same order + for (size_t i = 0; i < count; ++i) { + CHECK(hashlist.erase(i) == true); + } + CHECK(hashlist.getBlockCount() == 0); +} + +TEST_CASE("can init PageManager in empty flash", "[nvs]") +{ + PartitionEmulationFixture f(0, 4); + nvs::PageManager pm; + CHECK(pm.load(f.part(), 0, 4) == ESP_OK); +} + +TEST_CASE("PageManager adds page in the correct order", "[nvs]") +{ + const size_t pageCount = 8; + PartitionEmulationFixture f(0, pageCount); + uint32_t pageNo[pageCount] = { -1U, 50, 11, -1U, 23, 22, 24, 49}; + + for (uint32_t i = 0; i < pageCount; ++i) { + nvs::Page p; + p.load(f.part(), i); + if (pageNo[i] != -1U) { + p.setSeqNumber(pageNo[i]); + p.writeItem(1, "foo", 10U); + } + } + + nvs::PageManager pageManager; + CHECK(pageManager.load(f.part(), 0, pageCount) == ESP_OK); + + uint32_t lastSeqNo = 0; + for (auto it = std::begin(pageManager); it != std::end(pageManager); ++it) { + uint32_t seqNo; + CHECK(it->getSeqNumber(seqNo) == ESP_OK); + CHECK(seqNo > lastSeqNo); + } +} + +TEST_CASE("can init storage in empty flash", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + TEMPORARILY_DISABLED(f.emu.setBounds(4, 8);) + cout << "before check" << endl; + CHECK(storage.init(4, 4) == ESP_OK); + TEMPORARILY_DISABLED(s_perf << "Time to init empty storage (4 sectors): " << f.emu.getTotalTime() << " us" << std::endl;) +} + +TEST_CASE("storage doesn't add duplicates within one page", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + TEMPORARILY_DISABLED(f.emu.setBounds(4, 8);) + CHECK(storage.init(4, 4) == ESP_OK); + int bar = 0; + CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); + CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); + + nvs::Page page; + page.load(f.part(), 4); + CHECK(page.getUsedEntryCount() == 1); + CHECK(page.getErasedEntryCount() == 1); +} + +TEST_CASE("can write one item a thousand times", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + TEMPORARILY_DISABLED(f.emu.setBounds(4, 8);) + CHECK(storage.init(4, 4) == ESP_OK); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT * 4 * 2; ++i) { + REQUIRE(storage.writeItem(1, "i", static_cast(i)) == ESP_OK); + } + TEMPORARILY_DISABLED(s_perf << "Time to write one item a thousand times: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << " " << f.emu.getWriteOps() << " " << f.emu.getReadOps() << " " << f.emu.getWriteBytes() << " " << f.emu.getReadBytes() << ")" << std::endl;) +} + +TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + TEMPORARILY_DISABLED(f.emu.setBounds(4, 8);) + CHECK(storage.init(4, 4) == ESP_OK); + int bar = 0; + CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT; ++i) { + CHECK(storage.writeItem(1, "foo", static_cast(++bar)) == ESP_OK); + } + CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); + + nvs::Page page; + page.load(f.part(), 4); + CHECK(page.findItem(1, nvs::itemTypeOf(), "bar") == ESP_ERR_NVS_NOT_FOUND); + page.load(f.part(), 5); + CHECK(page.findItem(1, nvs::itemTypeOf(), "bar") == ESP_OK); +} + +TEST_CASE("storage can find items on second page if first is not fully written and has cached search data", "[nvs]") +{ + PartitionEmulationFixture f(0, 3); + nvs::Storage storage(f.part()); + CHECK(storage.init(0, 3) == ESP_OK); + int bar = 0; + uint8_t bigdata[(nvs::Page::CHUNK_MAX_SIZE - nvs::Page::ENTRY_SIZE) / 2] = {0}; + // write one big chunk of data + ESP_ERROR_CHECK(storage.writeItem(0, nvs::ItemType::BLOB, "1", bigdata, sizeof(bigdata))); + // write another big chunk of data + ESP_ERROR_CHECK(storage.writeItem(0, nvs::ItemType::BLOB, "2", bigdata, sizeof(bigdata))); + + // write third one; it will not fit into the first page + ESP_ERROR_CHECK(storage.writeItem(0, nvs::ItemType::BLOB, "3", bigdata, sizeof(bigdata))); + + size_t size; + ESP_ERROR_CHECK(storage.getItemDataSize(0, nvs::ItemType::BLOB, "1", size)); + CHECK(size == sizeof(bigdata)); + ESP_ERROR_CHECK(storage.getItemDataSize(0, nvs::ItemType::BLOB, "3", size)); + CHECK(size == sizeof(bigdata)); +} + +TEST_CASE("can write and read variable length data lots of times", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + TEMPORARILY_DISABLED(f.emu.setBounds(4, 8);) + CHECK(storage.init(4, 4) == ESP_OK); + const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; + char buf[sizeof(str) + 16]; + size_t len = strlen(str); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT * 4 * 2; ++i) { + CAPTURE(i); + CHECK(storage.writeItem(1, nvs::ItemType::SZ, "foobaar", str, len + 1) == ESP_OK); + CHECK(storage.writeItem(1, "foo", static_cast(i)) == ESP_OK); + + uint32_t value; + CHECK(storage.readItem(1, "foo", value) == ESP_OK); + CHECK(value == i); + + fill_n(buf, sizeof(buf), 0xff); + CHECK(storage.readItem(1, nvs::ItemType::SZ, "foobaar", buf, sizeof(buf)) == ESP_OK); + CHECK(memcmp(buf, str, strlen(str) + 1) == 0); + } + TEMPORARILY_DISABLED(s_perf << "Time to write one string and one integer a thousand times: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << " " << f.emu.getWriteOps() << " " << f.emu.getReadOps() << " " << f.emu.getWriteBytes() << " " << f.emu.getReadBytes() << ")" << std::endl;) +} + +TEST_CASE("can get length of variable length data", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + TEMPORARILY_DISABLED(f.emu.randomize(200);) + nvs::Storage storage(f.part()); + TEMPORARILY_DISABLED(f.emu.setBounds(4, 8);) + CHECK(storage.init(4, 4) == ESP_OK); + const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; + size_t len = strlen(str); + CHECK(storage.writeItem(1, nvs::ItemType::SZ, "foobaar", str, len + 1) == ESP_OK); + size_t dataSize; + CHECK(storage.getItemDataSize(1, nvs::ItemType::SZ, "foobaar", dataSize) == ESP_OK); + CHECK(dataSize == len + 1); + + CHECK(storage.writeItem(2, nvs::ItemType::BLOB, "foobaar", str, len) == ESP_OK); + CHECK(storage.getItemDataSize(2, nvs::ItemType::BLOB, "foobaar", dataSize) == ESP_OK); + CHECK(dataSize == len); +} + +TEST_CASE("can create namespaces", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + TEMPORARILY_DISABLED(f.emu.setBounds(4, 8);) + CHECK(storage.init(4, 4) == ESP_OK); + uint8_t nsi; + CHECK(storage.createOrOpenNamespace("wifi", false, nsi) == ESP_ERR_NVS_NOT_FOUND); + + CHECK(storage.createOrOpenNamespace("wifi", true, nsi) == ESP_OK); + nvs::Page page; + page.load(f.part(), 4); + CHECK(page.findItem(nvs::Page::NS_INDEX, nvs::ItemType::U8, "wifi") == ESP_OK); +} + +TEST_CASE("storage may become full", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + TEMPORARILY_DISABLED(f.emu.setBounds(4, 8);) + CHECK(storage.init(4, 4) == ESP_OK); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT * 3; ++i) { + char name[nvs::Item::MAX_KEY_LENGTH + 1]; + snprintf(name, sizeof(name), "key%05d", static_cast(i)); + REQUIRE(storage.writeItem(1, name, static_cast(i)) == ESP_OK); + } + REQUIRE(storage.writeItem(1, "foo", 10) == ESP_ERR_NVS_NOT_ENOUGH_SPACE); +} + +TEST_CASE("can modify an item on a page which will be erased", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + CHECK(storage.init(0, 2) == ESP_OK); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT * 3 + 1; ++i) { + REQUIRE(storage.writeItem(1, "foo", 42U) == ESP_OK); + } +} + +TEST_CASE("erase operations are distributed among sectors", "[nvs]") +{ + const size_t sectors = 6; + PartitionEmulationFixture f(0, sectors); + nvs::Storage storage(f.part()); + CHECK(storage.init(0, sectors) == ESP_OK); + + /* Fill some part of storage with static values */ + const size_t static_sectors = 2; + for (size_t i = 0; i < static_sectors * nvs::Page::ENTRY_COUNT; ++i) { + char name[nvs::Item::MAX_KEY_LENGTH]; + snprintf(name, sizeof(name), "static%d", (int) i); + REQUIRE(storage.writeItem(1, name, i) == ESP_OK); + } + + /* Now perform many write operations */ + const size_t write_ops = 2000; + for (size_t i = 0; i < write_ops; ++i) { + REQUIRE(storage.writeItem(1, "value", i) == ESP_OK); + } + + /* Check that erase counts are distributed between the remaining sectors */ + const size_t max_erase_cnt = write_ops / nvs::Page::ENTRY_COUNT / (sectors - static_sectors) + 1; + for (size_t i = 0; i < sectors; ++i) { + TEMPORARILY_DISABLED( + auto erase_cnt = f.emu.getSectorEraseCount(i); + INFO("Sector " << i << " erased " << erase_cnt); + CHECK(erase_cnt <= max_erase_cnt); + ) + } +} + +TEST_CASE("can erase items", "[nvs]") +{ + PartitionEmulationFixture f(0, 8); + nvs::Storage storage(f.part()); + CHECK(storage.init(0, 3) == ESP_OK); + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT * 2 - 3; ++i) { + char name[nvs::Item::MAX_KEY_LENGTH + 1]; + snprintf(name, sizeof(name), "key%05d", static_cast(i)); + REQUIRE(storage.writeItem(3, name, static_cast(i)) == ESP_OK); + } + CHECK(storage.writeItem(1, "foo", 32) == ESP_OK); + CHECK(storage.writeItem(2, "foo", 64) == ESP_OK); + CHECK(storage.eraseItem(2, "foo") == ESP_OK); + int val; + CHECK(storage.readItem(1, "foo", val) == ESP_OK); + CHECK(val == 32); + CHECK(storage.eraseNamespace(3) == ESP_OK); + CHECK(storage.readItem(2, "foo", val) == ESP_ERR_NVS_NOT_FOUND); + CHECK(storage.readItem(3, "key00222", val) == ESP_ERR_NVS_NOT_FOUND); +} + +TEST_CASE("readonly handle fails on writing", "[nvs]") +{ + PartitionEmulationFixture f(0, 10); + const char *str = "value 0123456789abcdef0123456789abcdef"; + const uint8_t blob[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; + + nvs_handle_t handle_1; + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + // first, creating namespace... + TEST_ESP_OK(nvs_open("ro_ns", NVS_READWRITE, &handle_1)); + nvs_close(handle_1); + + TEST_ESP_OK(nvs_open("ro_ns", NVS_READONLY, &handle_1)); + TEST_ESP_ERR(nvs_set_i32(handle_1, "key", 47), ESP_ERR_NVS_READ_ONLY); + TEST_ESP_ERR(nvs_set_str(handle_1, "key", str), ESP_ERR_NVS_READ_ONLY); + TEST_ESP_ERR(nvs_set_blob(handle_1, "key", blob, 8), ESP_ERR_NVS_READ_ONLY); + + nvs_close(handle_1); + + // without deinit it affects "nvs api tests" + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + +TEST_CASE("nvs api tests", "[nvs]") +{ + PartitionEmulationFixture f(0, 10); + TEMPORARILY_DISABLED(f.emu.randomize(100);) + + nvs_handle_t handle_1; + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + + TEST_ESP_ERR(nvs_open("namespace1", NVS_READWRITE, &handle_1), ESP_ERR_NVS_NOT_INITIALIZED); + for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { + TEMPORARILY_DISABLED(f.emu.erase(i);) + } + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); + + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1)); + TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678)); + TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789)); + + nvs_handle_t handle_2; + TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2)); + TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a)); + const char *str = "value 0123456789abcdef0123456789abcdef"; + TEST_ESP_OK(nvs_set_str(handle_2, "key", str)); + + int32_t v1; + TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1)); + CHECK(0x23456789 == v1); + + int32_t v2; + TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2)); + CHECK(0x3456789a == v2); + + char buf[strlen(str) + 1]; + size_t buf_len = sizeof(buf); + + size_t buf_len_needed; + TEST_ESP_OK(nvs_get_str(handle_2, "key", NULL, &buf_len_needed)); + CHECK(buf_len_needed == buf_len); + + size_t buf_len_short = buf_len - 1; + TEST_ESP_ERR(ESP_ERR_NVS_INVALID_LENGTH, nvs_get_str(handle_2, "key", buf, &buf_len_short)); + CHECK(buf_len_short == buf_len); + + size_t buf_len_long = buf_len + 1; + TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len_long)); + CHECK(buf_len_long == buf_len); + + TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len)); + + CHECK(0 == strcmp(buf, str)); + nvs_close(handle_1); + nvs_close(handle_2); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + +TEST_CASE("deinit partition doesn't affect other partition's open handles", "[nvs]") +{ + const char *OTHER_PARTITION_NAME = "other_part"; + PartitionEmulationFixture f(0, 10); + PartitionEmulationFixture f_other(0, 10, OTHER_PARTITION_NAME); + const char *str = "value 0123456789abcdef0123456789abcdef"; + const uint8_t blob[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; + + nvs_handle_t handle_1; + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + TEMPORARILY_DISABLED(f_other.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f_other.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + TEST_ESP_OK(nvs_open_from_partition(OTHER_PARTITION_NAME, "ns", NVS_READWRITE, &handle_1)); + + // Deinitializing must not interfere with the open handle from the other partition. + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); + + TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x3456789a)); + nvs_close(handle_1); + + TEST_ESP_OK(nvs_flash_deinit_partition(OTHER_PARTITION_NAME)); +} + +TEST_CASE("nvs iterator nvs_entry_find invalid parameter test", "[nvs]") +{ + nvs_iterator_t it = reinterpret_cast(0xbeef); + CHECK(nvs_entry_find(nullptr, NULL, NVS_TYPE_ANY, &it) == ESP_ERR_INVALID_ARG); + CHECK(nvs_entry_find("nvs", NULL, NVS_TYPE_ANY, nullptr) == ESP_ERR_INVALID_ARG); +} + +TEST_CASE("nvs iterator nvs_entry_find doesn't change iterator on parameter error", "[nvs]") +{ + nvs_iterator_t it = reinterpret_cast(0xbeef); + REQUIRE(nvs_entry_find(nullptr, NULL, NVS_TYPE_ANY, &it) == ESP_ERR_INVALID_ARG); + CHECK(it == reinterpret_cast(0xbeef)); + + it = nullptr; + REQUIRE(nvs_entry_find(nullptr, NULL, NVS_TYPE_ANY, &it) == ESP_ERR_INVALID_ARG); + CHECK(it == nullptr); +} + +TEST_CASE("nvs_entry_next return ESP_ERR_INVALID_ARG on parameter is NULL", "[nvs]") +{ + CHECK(nvs_entry_next(nullptr) == ESP_ERR_INVALID_ARG); +} + +TEST_CASE("nvs_entry_info fails with ESP_ERR_INVALID_ARG if a parameter is NULL", "[nvs]") +{ + nvs_iterator_t it = reinterpret_cast(0xbeef); + nvs_entry_info_t info; + CHECK(nvs_entry_info(it, nullptr) == ESP_ERR_INVALID_ARG); + CHECK(nvs_entry_info(nullptr, &info) == ESP_ERR_INVALID_ARG); +} + +TEST_CASE("nvs_entry_info doesn't change iterator on parameter error", "[nvs]") +{ + nvs_iterator_t it = reinterpret_cast(0xbeef); + nvs_entry_info_t info; + REQUIRE(nvs_entry_info(it, nullptr) == ESP_ERR_INVALID_ARG); + CHECK(it == reinterpret_cast(0xbeef)); + + it = nullptr; + REQUIRE(nvs_entry_info(it, nullptr) == ESP_ERR_INVALID_ARG); + CHECK(it == nullptr); +} + +TEST_CASE("nvs iterators tests", "[nvs]") +{ + PartitionEmulationFixture f(0, 5); + + const uint32_t NVS_FLASH_SECTOR = 0; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { + TEMPORARILY_DISABLED(f.emu.erase(i);) + } + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + nvs_iterator_t it; + nvs_entry_info_t info; + nvs_handle handle_1; + nvs_handle handle_2; + const uint32_t blob = 0x11223344; + const char *name_1 = "namespace1"; + const char *name_2 = "namespace2"; + TEST_ESP_OK(nvs_open(name_1, NVS_READWRITE, &handle_1)); + TEST_ESP_OK(nvs_open(name_2, NVS_READWRITE, &handle_2)); + + TEST_ESP_OK(nvs_set_i8(handle_1, "value1", -11)); + TEST_ESP_OK(nvs_set_u8(handle_1, "value2", 11)); + TEST_ESP_OK(nvs_set_i16(handle_1, "value3", 1234)); + TEST_ESP_OK(nvs_set_u16(handle_1, "value4", -1234)); + TEST_ESP_OK(nvs_set_i32(handle_1, "value5", -222)); + TEST_ESP_OK(nvs_set_i32(handle_1, "value6", -222)); + TEST_ESP_OK(nvs_set_i32(handle_1, "value7", -222)); + TEST_ESP_OK(nvs_set_u32(handle_1, "value8", 222)); + TEST_ESP_OK(nvs_set_u32(handle_1, "value9", 222)); + TEST_ESP_OK(nvs_set_str(handle_1, "value10", "foo")); + TEST_ESP_OK(nvs_set_blob(handle_1, "value11", &blob, sizeof(blob))); + TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -111)); + TEST_ESP_OK(nvs_set_i32(handle_2, "value2", -111)); + TEST_ESP_OK(nvs_set_i64(handle_2, "value3", -555)); + TEST_ESP_OK(nvs_set_u64(handle_2, "value4", 555)); + + auto entry_count = [](const char *part, const char *name, nvs_type_t type)-> int { + int count = 0; + nvs_iterator_t it = nullptr; + esp_err_t res = nvs_entry_find(part, name, type, &it); + for (count = 0; res == ESP_OK; count++) + { + res = nvs_entry_next(&it); + } + CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop or if no entry was found to begin with, + // res has to be ESP_ERR_NVS_NOT_FOUND or some internal error + // or programming error occurred + nvs_release_iterator(it); // unneccessary call but emphasizes the programming pattern + return count; + }; + + SECTION("No partition found return ESP_ERR_NVS_NOT_FOUND") { + CHECK(nvs_entry_find("", NULL, NVS_TYPE_ANY, &it) == ESP_ERR_NVS_NOT_FOUND); + } + + SECTION("No matching namespace found return ESP_ERR_NVS_NOT_FOUND") { + CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, "nonexistent", NVS_TYPE_ANY, &it) == ESP_ERR_NVS_NOT_FOUND); + } + + SECTION("nvs_entry_find sets iterator to null if no matching element found") { + it = reinterpret_cast(0xbeef); + REQUIRE(nvs_entry_find(NVS_DEFAULT_PART_NAME, "nonexistent", NVS_TYPE_I16, &it) == ESP_ERR_NVS_NOT_FOUND); + CHECK(it == nullptr); + } + + SECTION("Finding iterator means iterator is valid") { + it = nullptr; + CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, nullptr, NVS_TYPE_ANY, &it) == ESP_OK); + CHECK(it != nullptr); + nvs_release_iterator(it); + } + + SECTION("Return ESP_ERR_NVS_NOT_FOUND after iterating over last matching element") { + it = nullptr; + REQUIRE(nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I16, &it) == ESP_OK); + REQUIRE(it != nullptr); + CHECK(nvs_entry_next(&it) == ESP_ERR_NVS_NOT_FOUND); + } + + SECTION("Set iterator to NULL after iterating over last matching element") { + it = nullptr; + REQUIRE(nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I16, &it) == ESP_OK); + REQUIRE(it != nullptr); + REQUIRE(nvs_entry_next(&it) == ESP_ERR_NVS_NOT_FOUND); + CHECK(it == nullptr); + } + + SECTION("Number of entries found for specified namespace and type is correct") { + CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_ANY) == 15); + CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY) == 11); + CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I32) == 3); + CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_I32) == 5); + CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1); + } + + SECTION("New entry is not created when existing key-value pair is set") { + CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4); + TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -222)); + CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4); + } + + SECTION("Number of entries found decrease when entry is erased") { + CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1); + TEST_ESP_OK(nvs_erase_key(handle_2, "value4")); + CHECK(entry_count(NVS_DEFAULT_PART_NAME, "", NVS_TYPE_U64) == 0); + } + + SECTION("All fields of nvs_entry_info_t structure are correct") { + it = nullptr; + esp_err_t res = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I32, &it); + REQUIRE(res == ESP_OK); + string key = "value5"; + while (res == ESP_OK) { + REQUIRE(nvs_entry_info(it, &info) == ESP_OK); + + CHECK(string(name_1) == info.namespace_name); + CHECK(key == info.key); + CHECK(info.type == NVS_TYPE_I32); + + res = nvs_entry_next(&it); + key[5]++; + } + CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop, res has to be ESP_ERR_NVS_NOT_FOUND + // or some internal error or programming error occurred + CHECK(key == "value8"); + nvs_release_iterator(it); // unneccessary call but emphasizes the programming pattern + } + + SECTION("Entry info is not affected by subsequent erase") { + nvs_entry_info_t info_after_erase; + + CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY, &it) == ESP_OK); + REQUIRE(nvs_entry_info(it, &info) == ESP_OK); + TEST_ESP_OK(nvs_erase_key(handle_1, "value1")); + REQUIRE(nvs_entry_info(it, &info_after_erase) == ESP_OK); + CHECK(memcmp(&info, &info_after_erase, sizeof(info)) == 0); + nvs_release_iterator(it); + } + + SECTION("Entry info is not affected by subsequent set") { + nvs_entry_info_t info_after_set; + + CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY, &it) == ESP_OK); + REQUIRE(nvs_entry_info(it, &info) == ESP_OK); + TEST_ESP_OK(nvs_set_u8(handle_1, info.key, 44)); + REQUIRE(nvs_entry_info(it, &info_after_set) == ESP_OK); + CHECK(memcmp(&info, &info_after_set, sizeof(info)) == 0); + nvs_release_iterator(it); + } + + + SECTION("Iterating over multiple pages works correctly") { + nvs_handle handle_3; + const char *name_3 = "namespace3"; + const int entries_created = 250; + + TEST_ESP_OK(nvs_open(name_3, NVS_READWRITE, &handle_3)); + for (size_t i = 0; i < entries_created; i++) { + TEST_ESP_OK(nvs_set_u8(handle_3, to_string(i).c_str(), 123)); + } + + int entries_found = 0; + it = nullptr; + esp_err_t res = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_3, NVS_TYPE_ANY, &it); + while (res == ESP_OK) { + entries_found++; + res = nvs_entry_next(&it); + } + CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop, res has to be ESP_ERR_NVS_NOT_FOUND + // or some internal error or programming error occurred + CHECK(entries_created == entries_found); + + nvs_release_iterator(it); // unneccessary call but emphasizes the programming pattern + nvs_close(handle_3); + } + + SECTION("Iterating over multi-page blob works correctly") { + nvs_handle handle_3; + const char *name_3 = "namespace3"; + const uint8_t multipage_blob[4096 * 2] = { 0 }; + const int NUMBER_OF_ENTRIES_PER_PAGE = 125; + size_t occupied_entries; + + TEST_ESP_OK(nvs_open(name_3, NVS_READWRITE, &handle_3)); + nvs_set_blob(handle_3, "blob", multipage_blob, sizeof(multipage_blob)); + TEST_ESP_OK(nvs_get_used_entry_count(handle_3, &occupied_entries)); + CHECK(occupied_entries > NUMBER_OF_ENTRIES_PER_PAGE * 2); + + CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_3, NVS_TYPE_BLOB) == 1); + + nvs_close(handle_3); + } + + nvs_close(handle_1); + nvs_close(handle_2); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + +TEST_CASE("Iterator with not matching type iterates correctly", "[nvs]") +{ + PartitionEmulationFixture f(0, 5); + nvs_iterator_t it; + nvs_handle_t my_handle; + const char *NAMESPACE = "test_ns_4"; + + const uint32_t NVS_FLASH_SECTOR = 0; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { + TEMPORARILY_DISABLED(f.emu.erase(i);) + } + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + // writing string to namespace (a type which spans multiple entries) + TEST_ESP_OK(nvs_open(NAMESPACE, NVS_READWRITE, &my_handle)); + TEST_ESP_OK(nvs_set_str(my_handle, "test-string", "InitString0")); + TEST_ESP_OK(nvs_commit(my_handle)); + nvs_close(my_handle); + + CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, NAMESPACE, NVS_TYPE_I32, &it) == ESP_ERR_NVS_NOT_FOUND); + + // re-init to trigger cleaning up of broken items -> a corrupted string will be erased + nvs_flash_deinit(); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, NAMESPACE, NVS_TYPE_STR, &it) == ESP_OK); + nvs_release_iterator(it); + + // without deinit it affects "nvs api tests" + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + +TEST_CASE("wifi test", "[nvs]") +{ + PartitionEmulationFixture f(0, 10); + TEMPORARILY_DISABLED(f.emu.randomize(10);) + + const uint32_t NVS_FLASH_SECTOR = 5; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + nvs_handle_t misc_handle; + TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &misc_handle)); + char log[33]; + size_t log_size = sizeof(log); + TEST_ESP_ERR(nvs_get_str(misc_handle, "log", log, &log_size), ESP_ERR_NVS_NOT_FOUND); + strcpy(log, "foobarbazfizzz"); + TEST_ESP_OK(nvs_set_str(misc_handle, "log", log)); + + nvs_handle_t net80211_handle; + TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &net80211_handle)); + + uint8_t opmode = 2; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.opmode", &opmode), ESP_ERR_NVS_NOT_FOUND); + + TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.opmode", opmode)); + + uint8_t country = 0; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.country", &opmode), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.country", opmode)); + + char ssid[36]; + size_t size = sizeof(ssid); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND); + strcpy(ssid, "my android AP"); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.ssid", ssid, size)); + + char mac[6]; + size = sizeof(mac); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND); + memset(mac, 0xab, 6); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.mac", mac, size)); + + uint8_t authmode = 1; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.authmode", authmode)); + + char pswd[65]; + size = sizeof(pswd); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pswd", pswd, &size), ESP_ERR_NVS_NOT_FOUND); + strcpy(pswd, "`123456788990-="); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pswd", pswd, size)); + + char pmk[32]; + size = sizeof(pmk); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND); + memset(pmk, 1, size); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pmk", pmk, size)); + + uint8_t chan = 1; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.chan", &chan), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.chan", chan)); + + uint8_t autoconn = 1; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "auto.conn", &autoconn), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "auto.conn", autoconn)); + + uint8_t bssid_set = 1; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bssid.set", &bssid_set), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "bssid.set", bssid_set)); + + char bssid[6]; + size = sizeof(bssid); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.bssid", bssid, &size), ESP_ERR_NVS_NOT_FOUND); + memset(mac, 0xcd, 6); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.bssid", bssid, size)); + + uint8_t phym = 3; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phym", &phym), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phym", phym)); + + uint8_t phybw = 2; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phybw", &phybw), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phybw", phybw)); + + char apsw[2]; + size = sizeof(apsw); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apsw", apsw, &size), ESP_ERR_NVS_NOT_FOUND); + memset(apsw, 0x2, size); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apsw", apsw, size)); + + char apinfo[700]; + size = sizeof(apinfo); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apinfo", apinfo, &size), ESP_ERR_NVS_NOT_FOUND); + memset(apinfo, 0, size); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apinfo", apinfo, size)); + + size = sizeof(ssid); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND); + strcpy(ssid, "ESP_A2F340"); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.ssid", ssid, size)); + + size = sizeof(mac); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND); + memset(mac, 0xac, 6); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.mac", mac, size)); + + size = sizeof(pswd); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.passwd", pswd, &size), ESP_ERR_NVS_NOT_FOUND); + strcpy(pswd, ""); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.passwd", pswd, size)); + + size = sizeof(pmk); + TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND); + memset(pmk, 1, size); + TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.pmk", pmk, size)); + + chan = 6; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.chan", &chan), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.chan", chan)); + + authmode = 0; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.authmode", authmode)); + + uint8_t hidden = 0; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.hidden", &hidden), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.hidden", hidden)); + + uint8_t max_conn = 4; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.max.conn", &max_conn), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.max.conn", max_conn)); + + uint8_t bcn_interval = 2; + TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bcn_interval", &bcn_interval), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_set_u8(net80211_handle, "bcn_interval", bcn_interval)); + + //s_perf << "Time to simulate nvs init with wifi libs: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << "E " << f.emu.getWriteOps() << "W " << f.emu.getReadOps() << "R " << f.emu.getWriteBytes() << "Wb " << f.emu.getReadBytes() << "Rb)" << std::endl; + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + +extern "C" void nvs_dump(const char *partName); + +class RandomTest { + + static const size_t nKeys = 11; + int32_t v1 = 0, v2 = 0; + uint64_t v3 = 0, v4 = 0; + static const size_t strBufLen = 1024; + static const size_t smallBlobLen = nvs::Page::CHUNK_MAX_SIZE / 3; + static const size_t largeBlobLen = nvs::Page::CHUNK_MAX_SIZE * 3; + char v5[strBufLen], v6[strBufLen], v7[strBufLen], v8[strBufLen], v9[strBufLen]; + uint8_t v10[smallBlobLen], v11[largeBlobLen]; + bool written[nKeys]; + +public: + RandomTest() + { + std::fill_n(written, nKeys, false); + } + + template + esp_err_t doRandomThings(nvs_handle_t handle, TGen gen, size_t &count) + { + + const char *keys[] = {"foo", "bar", "longkey_0123456", "another key", "param1", "param2", "param3", "param4", "param5", "singlepage", "multipage"}; + const nvs::ItemType types[] = {nvs::ItemType::I32, nvs::ItemType::I32, nvs::ItemType::U64, nvs::ItemType::U64, nvs::ItemType::SZ, nvs::ItemType::SZ, nvs::ItemType::SZ, nvs::ItemType::SZ, nvs::ItemType::SZ, nvs::ItemType::BLOB, nvs::ItemType::BLOB}; + + void *values[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9, &v10, &v11}; + + const size_t nKeys = sizeof(keys) / sizeof(keys[0]); + static_assert(nKeys == sizeof(types) / sizeof(types[0]), ""); + static_assert(nKeys == sizeof(values) / sizeof(values[0]), ""); + + auto randomRead = [&](size_t index) -> esp_err_t { + switch (types[index]) + { + case nvs::ItemType::I32: { + int32_t val; + auto err = nvs_get_i32(handle, keys[index], &val); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (!written[index]) { + REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); + } else { + REQUIRE(err == ESP_OK); + REQUIRE(val == *reinterpret_cast(values[index])); + } + break; + } + + case nvs::ItemType::U64: { + uint64_t val; + auto err = nvs_get_u64(handle, keys[index], &val); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (!written[index]) { + REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); + } else { + REQUIRE(err == ESP_OK); + REQUIRE(val == *reinterpret_cast(values[index])); + } + break; + } + + case nvs::ItemType::SZ: { + char buf[strBufLen]; + size_t len = strBufLen; + auto err = nvs_get_str(handle, keys[index], buf, &len); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (!written[index]) { + REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); + } else { + REQUIRE(err == ESP_OK); + REQUIRE(strncmp(buf, reinterpret_cast(values[index]), strBufLen) == 0); + } + break; + } + + case nvs::ItemType::BLOB: { + uint32_t blobBufLen = 0; + if (strncmp(keys[index], "singlepage", sizeof("singlepage")) == 0) { + blobBufLen = smallBlobLen ; + } else { + blobBufLen = largeBlobLen ; + + } + uint8_t buf[blobBufLen]; + memset(buf, 0, blobBufLen); + + size_t len = blobBufLen; + auto err = nvs_get_blob(handle, keys[index], buf, &len); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (!written[index]) { + REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); + } else { + REQUIRE(err == ESP_OK); + REQUIRE(memcmp(buf, reinterpret_cast(values[index]), blobBufLen) == 0); + } + break; + } + + + default: + assert(0); + } + return ESP_OK; + }; + + auto randomWrite = [&](size_t index) -> esp_err_t { + switch (types[index]) + { + case nvs::ItemType::I32: { + int32_t val = static_cast(gen()); + + auto err = nvs_set_i32(handle, keys[index], val); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (err == ESP_ERR_NVS_REMOVE_FAILED) { + written[index] = true; + *reinterpret_cast(values[index]) = val; + return ESP_ERR_FLASH_OP_FAIL; + } + REQUIRE(err == ESP_OK); + written[index] = true; + *reinterpret_cast(values[index]) = val; + break; + } + + case nvs::ItemType::U64: { + uint64_t val = static_cast(gen()); + + auto err = nvs_set_u64(handle, keys[index], val); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (err == ESP_ERR_NVS_REMOVE_FAILED) { + written[index] = true; + *reinterpret_cast(values[index]) = val; + return ESP_ERR_FLASH_OP_FAIL; + } + REQUIRE(err == ESP_OK); + written[index] = true; + *reinterpret_cast(values[index]) = val; + break; + } + + case nvs::ItemType::SZ: { + char buf[strBufLen]; + size_t len = strBufLen; + + size_t strLen = gen() % (strBufLen - 1); + std::generate_n(buf, strLen, [&]() -> char { + const char c = static_cast(gen() % 127); + return (c < 32) ? 32 : c; + }); + buf[strLen] = 0; + + auto err = nvs_set_str(handle, keys[index], buf); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (err == ESP_ERR_NVS_REMOVE_FAILED) { + written[index] = true; + strncpy(reinterpret_cast(values[index]), buf, strBufLen); + return ESP_ERR_FLASH_OP_FAIL; + } + REQUIRE(err == ESP_OK); + written[index] = true; + strncpy(reinterpret_cast(values[index]), buf, strBufLen); + break; + } + + case nvs::ItemType::BLOB: { + uint32_t blobBufLen = 0; + if (strncmp(keys[index], "singlepage", sizeof("singlepage")) == 0) { + blobBufLen = smallBlobLen ; + } else { + blobBufLen = largeBlobLen ; + } + uint8_t buf[blobBufLen]; + memset(buf, 0, blobBufLen); + size_t blobLen = gen() % blobBufLen; + std::generate_n(buf, blobLen, [&]() -> uint8_t { + return static_cast(gen() % 256); + }); + + auto err = nvs_set_blob(handle, keys[index], buf, blobLen); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (err == ESP_ERR_NVS_REMOVE_FAILED) { + written[index] = true; + memcpy(reinterpret_cast(values[index]), buf, blobBufLen); + return ESP_ERR_FLASH_OP_FAIL; + } + REQUIRE(err == ESP_OK); + written[index] = true; + memcpy(reinterpret_cast(values[index]), buf, blobBufLen); + break; + } + + default: + assert(0); + } + return ESP_OK; + }; + + + for (; count != 0; --count) { + size_t index = gen() % (nKeys); + switch (gen() % 3) { + case 0: // read, 1/3 + if (randomRead(index) == ESP_ERR_FLASH_OP_FAIL) { + return ESP_ERR_FLASH_OP_FAIL; + } + break; + + default: // write, 2/3 + if (randomWrite(index) == ESP_ERR_FLASH_OP_FAIL) { + return ESP_ERR_FLASH_OP_FAIL; + } + break; + } + } + return ESP_OK; + } + + esp_err_t handleExternalWriteAtIndex(uint8_t index, const void *value, const size_t len ) + { + if (index == 9) { /* This is only done for small-page blobs for now*/ + if (len > smallBlobLen) { + return ESP_FAIL; + } + memcpy(v10, value, len); + written[index] = true; + return ESP_OK; + } else { + return ESP_FAIL; + } + } +}; + +TEST_CASE("monkey test", "[nvs][monkey]") +{ + std::random_device rd; + std::mt19937 gen(rd()); + uint32_t seed = 3; + gen.seed(seed); + + PartitionEmulationFixture f(0, 10); + TEMPORARILY_DISABLED(f.emu.randomize(seed);) + TEMPORARILY_DISABLED(f.emu.clearStats();) + + const uint32_t NVS_FLASH_SECTOR = 2; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + nvs_handle_t handle; + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); + RandomTest test; + size_t count = 1000; + CHECK(test.doRandomThings(handle, gen, count) == ESP_OK); + + //s_perf << "Monkey test: nErase=" << f.emu.getEraseOps() << " nWrite=" << f.emu.getWriteOps() << std::endl; + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + +TEST_CASE("test for memory leaks in open/set", "[leaks]") +{ + PartitionEmulationFixture f(0, 10); + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + for (int i = 0; i < 100000; ++i) { + nvs_handle_t light_handle = 0; + char lightbulb[1024] = {12, 13, 14, 15, 16}; + TEST_ESP_OK(nvs_open("light", NVS_READWRITE, &light_handle)); + TEST_ESP_OK(nvs_set_blob(light_handle, "key", lightbulb, sizeof(lightbulb))); + TEST_ESP_OK(nvs_commit(light_handle)); + nvs_close(light_handle); + } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + +TEST_CASE("read/write failure (TW8406)", "[nvs]") +{ + PartitionEmulationFixture f(0, 3); + nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3); + for (int attempts = 0; attempts < 3; ++attempts) { + int i = 0; + nvs_handle_t light_handle = 0; + char key[15] = {0}; + char data[76] = {12, 13, 14, 15, 16}; + uint8_t number = 20; + size_t data_len = sizeof(data); + + ESP_ERROR_CHECK(nvs_open("LIGHT", NVS_READWRITE, &light_handle)); + ESP_ERROR_CHECK(nvs_set_u8(light_handle, "RecordNum", number)); + for (i = 0; i < number; ++i) { + sprintf(key, "light%d", i); + ESP_ERROR_CHECK(nvs_set_blob(light_handle, key, data, sizeof(data))); + } + nvs_commit(light_handle); + + uint8_t get_number = 0; + ESP_ERROR_CHECK(nvs_get_u8(light_handle, "RecordNum", &get_number)); + REQUIRE(number == get_number); + for (i = 0; i < number; ++i) { + char data[76] = {0}; + sprintf(key, "light%d", i); + ESP_ERROR_CHECK(nvs_get_blob(light_handle, key, data, &data_len)); + } + nvs_close(light_handle); + } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + +TEST_CASE("nvs_flash_init checks for an empty page", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE; + uint8_t blob[blob_size] = {0}; + PartitionEmulationFixture f(0, 8); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 5) ); + nvs_handle_t handle; + TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); + // Fill first page + TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size) ); + // Fill second page + TEST_ESP_OK( nvs_set_blob(handle, "2a", blob, blob_size) ); + // Fill third page + TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, blob_size) ); + TEST_ESP_OK( nvs_commit(handle) ); + nvs_close(handle); + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); + // first two pages are now full, third one is writable, last two are empty + // init should fail + TEST_ESP_ERR( nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3), + ESP_ERR_NVS_NO_FREE_PAGES ); + + // in case this test fails, to not affect other tests + nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME); +} + +TEST_CASE("nvs page selection takes into account free entries also not just erased entries", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE / 2; + uint8_t blob[blob_size] = {0}; + PartitionEmulationFixture f(0, 3); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3) ); + nvs_handle_t handle; + TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); + // Fill first page + TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size / 3) ); + TEST_ESP_OK( nvs_set_blob(handle, "1b", blob, blob_size) ); + // Fill second page + TEST_ESP_OK( nvs_set_blob(handle, "2a", blob, blob_size) ); + TEST_ESP_OK( nvs_set_blob(handle, "2b", blob, blob_size) ); + + // The item below should be able to fit the first page. + TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, 4) ); + TEST_ESP_OK( nvs_commit(handle) ); + nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("calculate used and free space", "[nvs]") +{ + PartitionEmulationFixture f(0, 6); + nvs_flash_deinit(); + 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_t 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::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 6)); + + TEST_ESP_ERR(nvs_get_used_entry_count(handle, &h_count_entries), ESP_ERR_NVS_INVALID_HANDLE); + CHECK(h_count_entries == 0); + + nvs::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_t 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_t 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_t 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 + 4 == stat2.free_entries); + CHECK(stat1.namespace_count == 3); + CHECK(stat1.total_entries == stat2.total_entries); + CHECK(stat1.used_entries == 12); + + // 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 == 4); + + CHECK(stat1.used_entries == (h1_count_entries + h2_count_entries + h3_count_entries + stat1.namespace_count)); + + nvs_close(handle_3); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("Multi-page blobs are supported", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 2; + uint8_t blob[blob_size] = {0}; + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 5)); + nvs_handle_t handle; + TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle)); + TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); + TEST_ESP_OK(nvs_commit(handle)); + nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("Failures are handled while storing multi-page blobs", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 7; + uint8_t blob[blob_size] = {0}; + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 5)); + nvs_handle_t handle; + TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle)); + TEST_ESP_ERR(nvs_set_blob(handle, "abc", blob, blob_size), ESP_ERR_NVS_VALUE_TOO_LONG); + TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, nvs::Page::CHUNK_MAX_SIZE * 2)); + TEST_ESP_OK(nvs_commit(handle)); + nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("Reading multi-page blobs", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 3; + uint8_t blob[blob_size]; + uint8_t blob_read[blob_size]; + size_t read_size = blob_size; + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 5)); + nvs_handle_t handle; + memset(blob, 0x11, blob_size); + memset(blob_read, 0xee, blob_size); + TEST_ESP_OK(nvs_open("readTest", NVS_READWRITE, &handle)); + TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); + TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size)); + CHECK(memcmp(blob, blob_read, blob_size) == 0); + TEST_ESP_OK(nvs_commit(handle)); + nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("Modification of values for Multi-page blobs are supported", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 2; + uint8_t blob[blob_size] = {0}; + uint8_t blob_read[blob_size] = {0xfe};; + uint8_t blob2[blob_size] = {0x11}; + uint8_t blob3[blob_size] = {0x22}; + uint8_t blob4[blob_size] = { 0x33}; + size_t read_size = blob_size; + PartitionEmulationFixture f(0, 6); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 6) ); + nvs_handle_t handle; + memset(blob, 0x11, blob_size); + memset(blob2, 0x22, blob_size); + memset(blob3, 0x33, blob_size); + memset(blob4, 0x44, blob_size); + memset(blob_read, 0xff, blob_size); + TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); + TEST_ESP_OK( nvs_set_blob(handle, "abc", blob, blob_size) ); + TEST_ESP_OK( nvs_set_blob(handle, "abc", blob2, blob_size) ); + TEST_ESP_OK( nvs_set_blob(handle, "abc", blob3, blob_size) ); + TEST_ESP_OK( nvs_set_blob(handle, "abc", blob4, blob_size) ); + TEST_ESP_OK( nvs_get_blob(handle, "abc", blob_read, &read_size)); + CHECK(memcmp(blob4, blob_read, blob_size) == 0); + TEST_ESP_OK( nvs_commit(handle) ); + nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("Modification from single page blob to multi-page", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 3; + uint8_t blob[blob_size] = {0}; + uint8_t blob_read[blob_size] = {0xff}; + size_t read_size = blob_size; + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 5) ); + nvs_handle_t handle; + TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle) ); + TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, nvs::Page::CHUNK_MAX_SIZE / 2)); + TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); + TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size)); + CHECK(memcmp(blob, blob_read, blob_size) == 0); + TEST_ESP_OK(nvs_commit(handle) ); + nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("Modification from multi-page to single page", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 3; + uint8_t blob[blob_size] = {0}; + uint8_t blob_read[blob_size] = {0xff}; + size_t read_size = blob_size; + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 5) ); + nvs_handle_t handle; + TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle) ); + TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); + TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, nvs::Page::CHUNK_MAX_SIZE / 2)); + TEST_ESP_OK(nvs_set_blob(handle, "abc2", blob, blob_size)); + TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size)); + CHECK(memcmp(blob, blob_read, nvs::Page::CHUNK_MAX_SIZE) == 0); + TEST_ESP_OK(nvs_commit(handle) ); + nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("Multi-page blob erased using nvs_erase_key should not be found when probed for just length", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 3; + uint8_t blob[blob_size] = {0}; + size_t read_size = blob_size; + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 5)); + nvs_handle handle; + TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle)); + TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); + TEST_ESP_OK(nvs_erase_key(handle, "abc")); + TEST_ESP_ERR(nvs_get_blob(handle, "abc", NULL, &read_size), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_commit(handle)); + nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + + +TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 3 ; + uint8_t blob[blob_size] = {0x11}; + uint8_t blob2[blob_size] = {0x22}; + uint8_t blob3[blob_size] = {0x33}; + PartitionEmulationFixture f(0, 5); + nvs::Storage storage(f.part()); + + TEST_ESP_OK(storage.init(0, 5)); + + TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob))); + + + TEST_ESP_OK(storage.init(0, 5)); + /* Check that multi-page item is still available.**/ + TEST_ESP_OK(storage.readItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob))); + + TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::BLOB, "key2", blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE); + + nvs::Page p; + p.load(f.part(), 3); // This is where index will be placed. + p.erase(); + + TEST_ESP_OK(storage.init(0, 5)); + + TEST_ESP_ERR(storage.readItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob)), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, "key3", blob, sizeof(blob))); +} + +TEST_CASE("nvs blob fragmentation test", "[nvs]") +{ + PartitionEmulationFixture f(0, 4); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 4) ); + const size_t BLOB_SIZE = 3500; + uint8_t *blob = (uint8_t *) malloc(BLOB_SIZE); + CHECK(blob != NULL); + memset(blob, 0xEE, BLOB_SIZE); + const uint32_t magic = 0xff33eaeb; + nvs_handle_t h; + TEST_ESP_OK( nvs_open("blob_tests", NVS_READWRITE, &h) ); + for (int i = 0; i < 128; i++) { + INFO("Iteration " << i << "...\n"); + TEST_ESP_OK( nvs_set_u32(h, "magic", magic) ); + TEST_ESP_OK( nvs_set_blob(h, "blob", blob, BLOB_SIZE) ); + char seq_buf[16]; + sprintf(seq_buf, "seq%d", i); + TEST_ESP_OK( nvs_set_u32(h, seq_buf, i) ); + } + free(blob); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("nvs code handles errors properly when partition is near to full", "[nvs]") +{ + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE * 0.3 ; + uint8_t blob[blob_size] = {0x11}; + PartitionEmulationFixture f(0, 5); + nvs::Storage storage(f.part()); + char nvs_key[16] = ""; + + TEST_ESP_OK(storage.init(0, 5)); + + /* Four pages should fit roughly 12 blobs*/ + for (uint8_t count = 1; count <= 12; count++) { + sprintf(nvs_key, "key:%u", count); + TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, nvs_key, blob, sizeof(blob))); + } + + for (uint8_t count = 13; count <= 20; count++) { + sprintf(nvs_key, "key:%u", count); + TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::BLOB, nvs_key, blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE); + } +} + +TEST_CASE("Check for nvs version incompatibility", "[nvs]") +{ + PartitionEmulationFixture f(0, 3); + + int32_t val1 = 0x12345678; + nvs::Page p; + p.load(f.part(), 0); + TEST_ESP_OK(p.setVersion(nvs::Page::NVS_VERSION - 1)); + TEST_ESP_OK(p.writeItem(1, nvs::ItemType::I32, "foo", &val1, sizeof(val1))); + + TEST_ESP_ERR(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3), + ESP_ERR_NVS_NEW_VERSION_FOUND); + + // if something went wrong, clean up + nvs_flash_deinit_partition(f.part()->get_partition_name()); +} + +// TODO: leaks memory +TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") +{ + std::random_device rd; + std::mt19937 gen(rd()); + uint32_t seed = 3; + gen.seed(seed); + + PartitionEmulationFixture f(0, 10); + TEMPORARILY_DISABLED(f.emu.randomize(seed);) + TEMPORARILY_DISABLED(f.emu.clearStats();) + + const uint32_t NVS_FLASH_SECTOR = 2; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; + static const size_t smallBlobLen = nvs::Page::CHUNK_MAX_SIZE / 3; + + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + nvs_handle_t handle; + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); + RandomTest test; + + for ( uint8_t it = 0; it < 10; it++) { + size_t count = 200; + + /* Erase index and chunks for the blob with "singlepage" key */ + for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { + nvs::Page p; + p.load(f.part(), num); + p.eraseItem(1, nvs::ItemType::BLOB, "singlepage", nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY); + p.eraseItem(1, nvs::ItemType::BLOB_IDX, "singlepage", nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY); + p.eraseItem(1, nvs::ItemType::BLOB_DATA, "singlepage", nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY); + } + + /* Now write "singlepage" blob in old format*/ + for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { + nvs::Page p; + p.load(f.part(), num); + if (p.state() == nvs::Page::PageState::ACTIVE) { + uint8_t buf[smallBlobLen]; + size_t blobLen = gen() % smallBlobLen; + + if (blobLen > p.getVarDataTailroom()) { + blobLen = p.getVarDataTailroom(); + } + + std::generate_n(buf, blobLen, [&]() -> uint8_t { + return static_cast(gen() % 256); + }); + + TEST_ESP_OK(p.writeItem(1, nvs::ItemType::BLOB, "singlepage", buf, blobLen, nvs::Item::CHUNK_ANY)); + TEST_ESP_OK(p.findItem(1, nvs::ItemType::BLOB, "singlepage")); + test.handleExternalWriteAtIndex(9, buf, blobLen); // This assumes "singlepage" is always at index 9 + + break; + } + } + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); + /* Initialize again */ + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); + + /* Perform random things */ + auto res = test.doRandomThings(handle, gen, count); + if (res != ESP_OK) { + nvs_dump(NVS_DEFAULT_PART_NAME); + CHECK(0); + } + + /* Check that only one version is present for "singlepage". Its possible that last iteration did not write + * anything for "singlepage". So either old version or new version should be present.*/ + bool oldVerPresent = false, newVerPresent = false; + + for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { + nvs::Page p; + p.load(f.part(), num); + if (!oldVerPresent && p.findItem(1, nvs::ItemType::BLOB, "singlepage", nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY) == ESP_OK) { + oldVerPresent = true; + } + + if (!newVerPresent && p.findItem(1, nvs::ItemType::BLOB_IDX, "singlepage", nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY) == ESP_OK) { + newVerPresent = true; + } + } + CHECK(oldVerPresent != newVerPresent); + } + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); + //s_perf << "Monkey test: nErase=" << f.emu.getEraseOps() << " nWrite=" << f.emu.getWriteOps() << std::endl; +} + +TEST_CASE("Recovery from power-off during modification of blob present in old-format (same page)", "[nvs]") +{ + std::random_device rd; + std::mt19937 gen(rd()); + uint32_t seed = 3; + gen.seed(seed); + + PartitionEmulationFixture f(0, 3); + TEMPORARILY_DISABLED(f.emu.clearStats();) + + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3)); + + nvs_handle_t handle; + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); + + uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef}; + uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee}; + size_t buflen = sizeof(hexdata); + uint8_t buf[nvs::Page::CHUNK_MAX_SIZE]; + + /* Power-off when blob was being written on the same page where its old version in old format + * was present*/ + nvs::Page p; + p.load(f.part(), 0); + /* Write blob in old-format*/ + TEST_ESP_OK(p.writeItem(1, nvs::ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old))); + + /* Write blob in new format*/ + TEST_ESP_OK(p.writeItem(1, nvs::ItemType::BLOB_DATA, "singlepage", hexdata, sizeof(hexdata), 0)); + /* All pages are stored. Now store the index.*/ + nvs::Item item; + item.blobIndex.dataSize = sizeof(hexdata); + item.blobIndex.chunkCount = 1; + item.blobIndex.chunkStart = nvs::VerOffset::VER_0_OFFSET; + + TEST_ESP_OK(p.writeItem(1, nvs::ItemType::BLOB_IDX, "singlepage", item.data, sizeof(item.data))); + + TEST_ESP_OK(p.findItem(1, nvs::ItemType::BLOB, "singlepage")); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); + /* Initialize again */ + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3)); + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); + + TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen)); + CHECK(memcmp(buf, hexdata, buflen) == 0); + + nvs::Page p2; + p2.load(f.part(), 0); + TEST_ESP_ERR(p2.findItem(1, nvs::ItemType::BLOB, "singlepage"), ESP_ERR_NVS_TYPE_MISMATCH); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} + +TEST_CASE("Recovery from power-off during modification of blob present in old-format (different page)", "[nvs]") +{ + std::random_device rd; + std::mt19937 gen(rd()); + uint32_t seed = 3; + gen.seed(seed); + + PartitionEmulationFixture f(0, 3); + TEMPORARILY_DISABLED(f.emu.clearStats();) + + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3)); + + nvs_handle_t handle; + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); + + uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef}; + uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee}; + size_t buflen = sizeof(hexdata); + uint8_t buf[nvs::Page::CHUNK_MAX_SIZE]; + + + /* Power-off when blob was being written on the different page where its old version in old format + * was present*/ + nvs::Page p; + p.load(f.part(), 0); + /* Write blob in old-format*/ + TEST_ESP_OK(p.writeItem(1, nvs::ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old))); + + /* Write blob in new format*/ + TEST_ESP_OK(p.writeItem(1, nvs::ItemType::BLOB_DATA, "singlepage", hexdata, sizeof(hexdata), 0)); + /* All pages are stored. Now store the index.*/ + nvs::Item item; + item.blobIndex.dataSize = sizeof(hexdata); + item.blobIndex.chunkCount = 1; + item.blobIndex.chunkStart = nvs::VerOffset::VER_0_OFFSET; + p.markFull(); + nvs::Page p2; + p2.load(f.part(), 1); + p2.setSeqNumber(1); + + TEST_ESP_OK(p2.writeItem(1, nvs::ItemType::BLOB_IDX, "singlepage", item.data, sizeof(item.data))); + + TEST_ESP_OK(p.findItem(1, nvs::ItemType::BLOB, "singlepage")); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); + /* Initialize again */ + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3)); + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); + + TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen)); + CHECK(memcmp(buf, hexdata, buflen) == 0); + + nvs::Page p3; + p3.load(f.part(), 0); + TEST_ESP_ERR(p3.findItem(1, nvs::ItemType::BLOB, "singlepage"), ESP_ERR_NVS_NOT_FOUND); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name())); +} diff --git a/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_cxx_api.cpp similarity index 83% rename from components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp rename to components/nvs_flash/host_test/nvs_host_test/main/test_nvs_cxx_api.cpp index da1d006be2..84070fa6d1 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_cxx_api.cpp @@ -1,30 +1,16 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// 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. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "catch.hpp" #include #include -#include "nvs_test_api.h" #include "nvs_handle_simple.hpp" #include "nvs_partition_manager.hpp" -#include "spi_flash_emulation.h" - #include "test_fixtures.hpp" - #include -using namespace std; - TEST_CASE("NVSHandleSimple CXX api open invalid arguments", "[nvs cxx]") { const uint32_t NVS_FLASH_SECTOR = 6; @@ -34,7 +20,7 @@ TEST_CASE("NVSHandleSimple CXX api open invalid arguments", "[nvs cxx]") shared_ptr handle; REQUIRE(nvs::NVSPartitionManager::get_instance()-> - init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); + init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); handle = nvs::open_nvs_handle_from_partition(nullptr, "ns_1", NVS_READWRITE, &result); CHECK(result == ESP_ERR_INVALID_ARG); @@ -49,7 +35,9 @@ TEST_CASE("NVSHandleSimple CXX api open invalid arguments", "[nvs cxx]") TEST_CASE("NVSHandleSimple CXX api open partition uninitialized", "[nvs cxx]") { - SpiFlashEmulator emu(10); + uint8_t *p_part_desc_addr_start; + CHECK(esp_partition_file_mmap((const uint8_t **)&p_part_desc_addr_start) == ESP_OK); + esp_err_t result; shared_ptr handle; @@ -67,7 +55,7 @@ TEST_CASE("NVSHandleSimple CXX api open successful", "[nvs cxx]") esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -93,7 +81,7 @@ TEST_CASE("NVSHandleSimple CXX api open default part successful", "[nvs cxx]") esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -119,7 +107,7 @@ TEST_CASE("NVSHandleSimple CXX api open default part ns NULL", "[nvs cxx]") esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -142,7 +130,7 @@ TEST_CASE("NVSHandleSimple CXX api read/write string", "[nvs cxx]") esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -172,7 +160,7 @@ TEST_CASE("NVSHandleSimple CXX api read/write blob", "[nvs cxx]") esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); diff --git a/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_handle.cpp b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_handle.cpp new file mode 100644 index 0000000000..6c6e35e1d1 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_handle.cpp @@ -0,0 +1,268 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "catch.hpp" +#include +#include +#include "nvs_handle_simple.hpp" +#include "nvs_partition_manager.hpp" +#include "test_fixtures.hpp" +#include +#include + +#define TEMPORARILY_DISABLED(x) + +TEST_CASE("NVSHandleSimple closes its reference in PartitionManager", "[partition_mgr]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + PartitionEmulationFixture f(0, 10, "test"); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); + + nvs::NVSHandleSimple *handle; + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle) == ESP_OK); + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); + + delete handle; + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); + +} + +TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[partition_mgr]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + PartitionEmulationFixture f(0, 10, "test"); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); + + nvs::NVSHandleSimple *handle1; + nvs::NVSHandleSimple *handle2; + + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle1) == ESP_OK); + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle2) == ESP_OK); + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 2); + + delete handle1; + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); + + delete handle2; + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); + +} + +TEST_CASE("NVSHandleSimple readonly fails", "[partition_mgr]") +{ + PartitionEmulationFixture f(0, 10); + + nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME); + nvs::NVSHandleSimple *handle_1; + nvs::NVSHandleSimple *handle_2; + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + CHECK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); + + // first, creating namespace... + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle_1) == ESP_OK); + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); + + delete handle_1; + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READONLY, &handle_2) == ESP_OK); + CHECK(handle_2->set_item("key", 47) == ESP_ERR_NVS_READ_ONLY); + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); + + delete handle_2; + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); + // without deinit it affects "nvs api tests" + CHECK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); +} + +TEST_CASE("NVSHandleSimple set/get char", "[partition_mgr]") +{ + enum class TestEnum : char { + FOO = -1, + BEER, + BAR + }; + + PartitionEmulationFixture f(0, 10); + + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + nvs::NVSHandleSimple *handle; + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); + + char test_e = 'a'; + char test_e_read = 'z'; + + CHECK(handle->set_item("key", test_e) == ESP_OK); + + CHECK(handle->get_item("key", test_e_read) == ESP_OK); + CHECK(test_e == test_e_read); + + delete handle; + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); +} + +TEST_CASE("NVSHandleSimple correctly sets/gets int enum", "[partition_mgr]") +{ + enum class TestEnum : int { + FOO, + BAR + }; + + PartitionEmulationFixture f(0, 10); + + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + nvs::NVSHandleSimple *handle; + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); + + TestEnum test_e = TestEnum::BAR; + TestEnum test_e_read = TestEnum::FOO; + + CHECK(handle->set_item("key", test_e) == ESP_OK); + + CHECK(handle->get_item("key", test_e_read) == ESP_OK); + CHECK(test_e == test_e_read); + + delete handle; + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); +} + +TEST_CASE("NVSHandleSimple correctly sets/gets int enum with negative values", "[partition_mgr]") +{ + enum class TestEnum : int { + FOO = -1, + BEER, + BAR + }; + + PartitionEmulationFixture f(0, 10); + + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + nvs::NVSHandleSimple *handle; + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); + + TestEnum test_e = TestEnum::FOO; + TestEnum test_e_read = TestEnum::BEER; + + CHECK(handle->set_item("key", test_e) == ESP_OK); + + CHECK(handle->get_item("key", test_e_read) == ESP_OK); + CHECK(test_e == test_e_read); + + delete handle; + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); +} + +TEST_CASE("NVSHandleSimple correctly sets/gets uint8_t enum", "[partition_mgr]") +{ + enum class TestEnum : uint8_t { + FOO, + BAR + }; + + PartitionEmulationFixture f(0, 10); + + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + nvs::NVSHandleSimple *handle; + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); + + TestEnum test_e = TestEnum::BAR; + TestEnum test_e_read = TestEnum::FOO; + + CHECK(handle->set_item("key", test_e) == ESP_OK); + + CHECK(handle->get_item("key", test_e_read) == ESP_OK); + CHECK(test_e == test_e_read); + + delete handle; + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); +} + +TEST_CASE("NVSHandleSimple correctly sets/gets char enum", "[partition_mgr]") +{ + enum class TestEnum : char { + FOO = -1, + BEER, + BAR + }; + + PartitionEmulationFixture f(0, 10); + + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + nvs::NVSHandleSimple *handle; + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); + + TestEnum test_e = TestEnum::BAR; + TestEnum test_e_read = TestEnum::FOO; + + CHECK(handle->set_item("key", test_e) == ESP_OK); + + CHECK(handle->get_item("key", test_e_read) == ESP_OK); + CHECK(test_e == test_e_read); + + delete handle; + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); +} diff --git a/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_initialization.cpp b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_initialization.cpp new file mode 100644 index 0000000000..2482129535 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_initialization.cpp @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "catch.hpp" +#include "nvs.hpp" +#include "nvs_partition_manager.hpp" +#include "nvs_partition.hpp" +#include "test_fixtures.hpp" +#include + +TEST_CASE("nvs_flash_init_partition_ptr fails due to nullptr arg", "[nvs_custom_part]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + uint8_t *p_part_desc_addr_start; + CHECK(esp_partition_file_mmap((const uint8_t **)&p_part_desc_addr_start) == ESP_OK); + + CHECK(nvs_flash_init_partition_ptr(nullptr) == ESP_ERR_INVALID_ARG); +} + +TEST_CASE("nvs_flash_init_partition_ptr inits one partition", "[nvs_custom_part]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + uint8_t *p_part_desc_addr_start; + CHECK(esp_partition_file_mmap((const uint8_t **)&p_part_desc_addr_start) == ESP_OK); + + esp_partition_t partition = {}; + strcpy(partition.label, "test"); + partition.address = NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE; + partition.size = NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE; + + CHECK(nvs_flash_init_partition_ptr(&partition) == ESP_OK); + CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); + CHECK(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); +} diff --git a/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_storage.cpp b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_storage.cpp new file mode 100644 index 0000000000..7e19ce7d5d --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs_storage.cpp @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "catch.hpp" +#include +#include "nvs_storage.hpp" +#include "nvs_partition_manager.hpp" +#include "test_fixtures.hpp" +#include + +TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs_storage]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + PartitionEmulationFixture f(0, 10, "test"); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + uint8_t blob [] = {0x0, 0x1, 0x2, 0x3}; + uint8_t blob_new [] = {0x3, 0x2, 0x1, 0x0}; + nvs::Storage *storage = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test"); + uint8_t ns_index; + storage->createOrOpenNamespace("test_ns", true, ns_index); + + CHECK(storage->writeItem(ns_index, nvs::ItemType::BLOB, "test_blob", blob, sizeof(blob)) == ESP_OK); + + // changing provokes a blob with version offset 1 (VerOffset::VER_1_OFFSET) + CHECK(storage->writeItem(ns_index, nvs::ItemType::BLOB, "test_blob", blob_new, sizeof(blob_new)) == ESP_OK); + + nvs_opaque_iterator_t it; + it.storage = storage; + it.type = NVS_TYPE_ANY; + + // Central check: does the iterator recognize the blob with version 1? + REQUIRE(storage->findEntry(&it, "test_ns")); + + CHECK(string(it.entry_info.namespace_name) == string("test_ns")); + CHECK(string(it.entry_info.key) == string("test_blob")); + CHECK(it.entry_info.type == NVS_TYPE_BLOB); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); +} diff --git a/components/nvs_flash/host_test/nvs_host_test/main/test_partition_manager.cpp b/components/nvs_flash/host_test/nvs_host_test/main/test_partition_manager.cpp new file mode 100644 index 0000000000..2a67579a87 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_partition_manager.cpp @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "catch.hpp" +#include +#include +#include "nvs_handle_simple.hpp" +#include "nvs_partition_manager.hpp" +#include "nvs_test_api.h" +#include "test_fixtures.hpp" + +TEST_CASE("Partition manager initializes storage", "[partition_mgr]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + PartitionEmulationFixture f(0, 10, "test"); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); + CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(f.part()->get_partition_name()) == ESP_OK); +} + +TEST_CASE("Partition manager de-initializes storage", "[partition_mgr]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + PartitionEmulationFixture f(0, 10, "test"); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); + CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); + CHECK(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); + CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test") == nullptr); +} + +TEST_CASE("Partition manager open fails on null handle", "[partition_mgr]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + PartitionEmulationFixture f(0, 10, "test"); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + CHECK(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, nullptr) + == ESP_ERR_INVALID_ARG); + + nvs::NVSPartitionManager::get_instance()->deinit_partition("test"); +} + +TEST_CASE("Partition manager invalidates handle on partition de-init", "[partition_mgr]") +{ + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + PartitionEmulationFixture f(0, 10, "test"); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + == ESP_OK); + + nvs::NVSHandleSimple *handle; + REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle) == ESP_OK); + CHECK(handle->erase_all() == ESP_OK); + + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); + + CHECK(handle->erase_all() == ESP_ERR_NVS_INVALID_HANDLE); + + delete handle; +} diff --git a/components/nvs_flash/host_test/nvs_host_test/sdkconfig.defaults b/components/nvs_flash/host_test/nvs_host_test/sdkconfig.defaults new file mode 100644 index 0000000000..89c65632c9 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_host_test/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_IDF_TARGET="linux" +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n diff --git a/components/nvs_flash/test_nvs_host/Makefile b/components/nvs_flash/test_nvs_host/Makefile index 0e238d0962..be4e5c46b3 100644 --- a/components/nvs_flash/test_nvs_host/Makefile +++ b/components/nvs_flash/test_nvs_host/Makefile @@ -21,12 +21,8 @@ SOURCE_FILES = \ test_spi_flash_emulation.cpp \ test_intrusive_list.cpp \ test_nvs.cpp \ - test_partition_manager.cpp \ - test_nvs_handle.cpp \ - test_nvs_storage.cpp \ test_nvs_partition.cpp \ - test_nvs_cxx_api.cpp \ - test_nvs_initialization.cpp \ + test_partition_manager.cpp \ main.cpp SOURCE_FILES_C = ../../esp_rom/linux/esp_rom_crc.c ../../esp_common/src/esp_err_check_linux.c diff --git a/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp b/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp index 802256c1a9..8d1a4a14cd 100644 --- a/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp +++ b/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp @@ -1,20 +1,11 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// 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. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "esp_partition.h" #include "spi_flash_emulation.h" - static SpiFlashEmulator* s_emulator = nullptr; void spi_flash_emulator_set(SpiFlashEmulator* e) diff --git a/components/nvs_flash/test_nvs_host/spi_flash_emulation.h b/components/nvs_flash/test_nvs_host/spi_flash_emulation.h index f410434a5f..c80149f1e2 100644 --- a/components/nvs_flash/test_nvs_host/spi_flash_emulation.h +++ b/components/nvs_flash/test_nvs_host/spi_flash_emulation.h @@ -13,11 +13,6 @@ #include "spi_flash_mmap.h" #include "catch.hpp" -using std::copy; -using std::begin; -using std::end; -using std::fill_n; - class SpiFlashEmulator; void spi_flash_emulator_set(SpiFlashEmulator*); diff --git a/components/nvs_flash/test_nvs_host/test_compressed_enum_table.cpp b/components/nvs_flash/test_nvs_host/test_compressed_enum_table.cpp index 919a415ef2..8e9781637f 100644 --- a/components/nvs_flash/test_nvs_host/test_compressed_enum_table.cpp +++ b/components/nvs_flash/test_nvs_host/test_compressed_enum_table.cpp @@ -7,7 +7,6 @@ #include "compressed_enum_table.hpp" #include - TEST_CASE("test if CompressedEnumTable works as expected", "[enumtable]") { @@ -46,5 +45,4 @@ TEST_CASE("test if CompressedEnumTable works as expected", "[enumtable]") // h 9 3 9 0 9 2 4 9 CHECK(table.data()[0] == 0x93909249); - } diff --git a/components/nvs_flash/test_nvs_host/test_nvs.cpp b/components/nvs_flash/test_nvs_host/test_nvs.cpp index d71c6ec961..f057d2913b 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs.cpp @@ -19,18 +19,14 @@ #include #include #include - #include "test_fixtures.hpp" #define TEST_ESP_ERR(rc, res) CHECK((rc) == (res)) #define TEST_ESP_OK(rc) CHECK((rc) == ESP_OK) -using namespace std; -using namespace nvs; - stringstream s_perf; -void dumpBytes(const uint8_t* data, size_t count) +void dumpBytes(const uint8_t *data, size_t count) { for (uint32_t i = 0; i < count; ++i) { if (i % 32 == 0) { @@ -43,537 +39,26 @@ void dumpBytes(const uint8_t* data, size_t count) } } -TEST_CASE("crc32 behaves as expected", "[nvs]") -{ - Item item1; - item1.datatype = ItemType::I32; - item1.nsIndex = 1; - item1.crc32 = 0; - item1.chunkIndex = 0xff; - fill_n(item1.key, sizeof(item1.key), 0xbb); - fill_n(item1.data, sizeof(item1.data), 0xaa); - - auto crc32_1 = item1.calculateCrc32(); - - Item item2 = item1; - item2.crc32 = crc32_1; - - CHECK(crc32_1 == item2.calculateCrc32()); - - item2 = item1; - item2.nsIndex = 2; - CHECK(crc32_1 != item2.calculateCrc32()); - - item2 = item1; - item2.datatype = ItemType::U32; - CHECK(crc32_1 != item2.calculateCrc32()); - - item2 = item1; - strncpy(item2.key, "foo", Item::MAX_KEY_LENGTH); - CHECK(crc32_1 != item2.calculateCrc32()); -} - -TEST_CASE("Page starting with empty flash is in uninitialized state", "[nvs]") -{ - PartitionEmulationFixture f; - Page page; - CHECK(page.state() == Page::PageState::INVALID); - CHECK(page.load(&f.part, 0) == ESP_OK); - CHECK(page.state() == Page::PageState::UNINITIALIZED); -} - -TEST_CASE("Page can distinguish namespaces", "[nvs]") -{ - PartitionEmulationFixture f; - Page page; - CHECK(page.load(&f.part, 0) == ESP_OK); - int32_t val1 = 0x12345678; - CHECK(page.writeItem(1, ItemType::I32, "intval1", &val1, sizeof(val1)) == ESP_OK); - int32_t val2 = 0x23456789; - CHECK(page.writeItem(2, ItemType::I32, "intval1", &val2, sizeof(val2)) == ESP_OK); - - int32_t readVal; - CHECK(page.readItem(2, ItemType::I32, "intval1", &readVal, sizeof(readVal)) == ESP_OK); - CHECK(readVal == val2); - -} - -TEST_CASE("Page reading with different type causes type mismatch error", "[nvs]") -{ - PartitionEmulationFixture f; - Page page; - CHECK(page.load(&f.part, 0) == ESP_OK); - int32_t val = 0x12345678; - CHECK(page.writeItem(1, ItemType::I32, "intval1", &val, sizeof(val)) == ESP_OK); - CHECK(page.readItem(1, ItemType::U32, "intval1", &val, sizeof(val)) == ESP_ERR_NVS_TYPE_MISMATCH); -} - -TEST_CASE("Page when erased, it's state becomes UNITIALIZED", "[nvs]") -{ - PartitionEmulationFixture f; - Page page; - CHECK(page.load(&f.part, 0) == ESP_OK); - int32_t val = 0x12345678; - CHECK(page.writeItem(1, ItemType::I32, "intval1", &val, sizeof(val)) == ESP_OK); - CHECK(page.erase() == ESP_OK); - CHECK(page.state() == Page::PageState::UNINITIALIZED); -} - -TEST_CASE("Page when writing and erasing, used/erased counts are updated correctly", "[nvs]") -{ - PartitionEmulationFixture f; - Page page; - CHECK(page.load(&f.part, 0) == ESP_OK); - CHECK(page.getUsedEntryCount() == 0); - CHECK(page.getErasedEntryCount() == 0); - uint32_t foo1 = 0; - CHECK(page.writeItem(1, "foo1", foo1) == ESP_OK); - CHECK(page.getUsedEntryCount() == 1); - CHECK(page.writeItem(2, "foo1", foo1) == ESP_OK); - CHECK(page.getUsedEntryCount() == 2); - CHECK(page.eraseItem(2, "foo1") == ESP_OK); - CHECK(page.getUsedEntryCount() == 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", (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", (long int)i); - CHECK(page.eraseItem(1, itemTypeOf(), name) == ESP_OK); - } - CHECK(page.getUsedEntryCount() == 1); - CHECK(page.getErasedEntryCount() == Page::ENTRY_COUNT - 1); -} - -TEST_CASE("Page when page is full, adding an element fails", "[nvs]") -{ - PartitionEmulationFixture f; - Page page; - CHECK(page.load(&f.part, 0) == ESP_OK); - for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) { - char name[16]; - 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); -} - -TEST_CASE("Page maintains its seq number") -{ - PartitionEmulationFixture f; - { - Page page; - CHECK(page.load(&f.part, 0) == ESP_OK); - CHECK(page.setSeqNumber(123) == ESP_OK); - int32_t val = 42; - CHECK(page.writeItem(1, ItemType::I32, "dummy", &val, sizeof(val)) == ESP_OK); - } - { - Page page; - CHECK(page.load(&f.part, 0) == ESP_OK); - uint32_t seqno; - CHECK(page.getSeqNumber(seqno) == ESP_OK); - CHECK(seqno == 123); - } -} - -TEST_CASE("Page can write and read variable length data", "[nvs]") -{ - PartitionEmulationFixture f; - Page page; - CHECK(page.load(&f.part, 0) == ESP_OK); - const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; - size_t len = strlen(str); - CHECK(page.writeItem(1, "stuff1", 42) == ESP_OK); - CHECK(page.writeItem(1, "stuff2", 1) == ESP_OK); - CHECK(page.writeItem(1, ItemType::SZ, "foobaar", str, len + 1) == ESP_OK); - CHECK(page.writeItem(1, "stuff3", 2) == ESP_OK); - CHECK(page.writeItem(1, ItemType::BLOB, "baz", str, len) == ESP_OK); - CHECK(page.writeItem(1, "stuff4", 0x7abbccdd) == ESP_OK); - - char buf[sizeof(str) + 16]; - int32_t value; - CHECK(page.readItem(1, "stuff1", value) == ESP_OK); - CHECK(value == 42); - CHECK(page.readItem(1, "stuff2", value) == ESP_OK); - CHECK(value == 1); - CHECK(page.readItem(1, "stuff3", value) == ESP_OK); - CHECK(value == 2); - CHECK(page.readItem(1, "stuff4", value) == ESP_OK); - CHECK(value == 0x7abbccdd); - - fill_n(buf, sizeof(buf), 0xff); - CHECK(page.readItem(1, ItemType::SZ, "foobaar", buf, sizeof(buf)) == ESP_OK); - CHECK(memcmp(buf, str, strlen(str) + 1) == 0); - - fill_n(buf, sizeof(buf), 0xff); - CHECK(page.readItem(1, ItemType::BLOB, "baz", buf, sizeof(buf)) == ESP_OK); - CHECK(memcmp(buf, str, strlen(str)) == 0); -} - -TEST_CASE("Page different key names are distinguished even if the pointer is the same", "[nvs]") -{ - PartitionEmulationFixture f; - Page page; - TEST_ESP_OK(page.load(&f.part, 0)); - TEST_ESP_OK(page.writeItem(1, "i1", 1)); - TEST_ESP_OK(page.writeItem(1, "i2", 2)); - int32_t value; - char keyname[10] = {0}; - for (int i = 0; i < 2; ++i) { - strncpy(keyname, "i1", sizeof(keyname) - 1); - TEST_ESP_OK(page.readItem(1, keyname, value)); - CHECK(value == 1); - strncpy(keyname, "i2", sizeof(keyname) - 1); - TEST_ESP_OK(page.readItem(1, keyname, value)); - CHECK(value == 2); - } -} - -TEST_CASE("Page validates key size", "[nvs]") -{ - PartitionEmulationFixture f(0, 4); - Page page; - TEST_ESP_OK(page.load(&f.part, 0)); - // 16-character key fails - TEST_ESP_ERR(page.writeItem(1, "0123456789123456", 1), ESP_ERR_NVS_KEY_TOO_LONG); - // 15-character key is okay - TEST_ESP_OK(page.writeItem(1, "012345678912345", 1)); -} - -TEST_CASE("Page validates blob size", "[nvs]") -{ - PartitionEmulationFixture f(0, 4); - Page page; - TEST_ESP_OK(page.load(&f.part, 0)); - - char buf[4096] = { 0 }; - // There are two potential errors here: - // - not enough space in the page (because one value has been written already) - // - value is too long - // Check that the second one is actually returned. - TEST_ESP_ERR(page.writeItem(1, ItemType::BLOB, "2", buf, Page::ENTRY_COUNT * Page::ENTRY_SIZE), ESP_ERR_NVS_VALUE_TOO_LONG); - // Should fail as well - TEST_ESP_ERR(page.writeItem(1, ItemType::BLOB, "2", buf, Page::CHUNK_MAX_SIZE + 1), ESP_ERR_NVS_VALUE_TOO_LONG); - TEST_ESP_OK(page.writeItem(1, ItemType::BLOB, "2", buf, Page::CHUNK_MAX_SIZE)); -} TEST_CASE("Page handles invalid CRC of variable length items", "[nvs][cur]") { PartitionEmulationFixture f(0, 4); { - Page page; + nvs::Page page; TEST_ESP_OK(page.load(&f.part, 0)); char buf[128] = {0}; - TEST_ESP_OK(page.writeItem(1, ItemType::BLOB, "1", buf, sizeof(buf))); + TEST_ESP_OK(page.writeItem(1, nvs::ItemType::BLOB, "1", buf, sizeof(buf))); } // corrupt header of the item (64 is the offset of the first item in page) uint32_t overwrite_buf = 0; f.emu.write(64, &overwrite_buf, 4); // load page again { - Page page; + nvs::Page page; TEST_ESP_OK(page.load(&f.part, 0)); } } -class HashListTestHelper : public HashList -{ - public: - size_t getBlockCount() - { - return mBlockList.size(); - } -}; - -TEST_CASE("HashList is cleaned up as soon as items are erased", "[nvs]") -{ - HashListTestHelper hashlist; - // Add items - const size_t count = 128; - for (size_t i = 0; i < count; ++i) { - char key[16]; - snprintf(key, sizeof(key), "i%ld", (long int)i); - Item item(1, ItemType::U32, 1, key); - hashlist.insert(item, i); - } - INFO("Added " << count << " items, " << hashlist.getBlockCount() << " blocks"); - // Remove them in reverse order - for (size_t i = count; i > 0; --i) { - // Make sure that the element existed before it's erased - CHECK(hashlist.erase(i - 1) == true); - } - CHECK(hashlist.getBlockCount() == 0); - // Add again - for (size_t i = 0; i < count; ++i) { - char key[16]; - snprintf(key, sizeof(key), "i%ld", (long int)i); - Item item(1, ItemType::U32, 1, key); - hashlist.insert(item, i); - } - INFO("Added " << count << " items, " << hashlist.getBlockCount() << " blocks"); - // Remove them in the same order - for (size_t i = 0; i < count; ++i) { - CHECK(hashlist.erase(i) == true); - } - CHECK(hashlist.getBlockCount() == 0); -} - -TEST_CASE("can init PageManager in empty flash", "[nvs]") -{ - PartitionEmulationFixture f(0, 4); - PageManager pm; - CHECK(pm.load(&f.part, 0, 4) == ESP_OK); -} - -TEST_CASE("PageManager adds page in the correct order", "[nvs]") -{ - const size_t pageCount = 8; - PartitionEmulationFixture f(0, pageCount); - uint32_t pageNo[pageCount] = { -1U, 50, 11, -1U, 23, 22, 24, 49}; - - for (uint32_t i = 0; i < pageCount; ++i) { - Page p; - p.load(&f.part, i); - if (pageNo[i] != -1U) { - p.setSeqNumber(pageNo[i]); - p.writeItem(1, "foo", 10U); - } - } - - PageManager pageManager; - CHECK(pageManager.load(&f.part, 0, pageCount) == ESP_OK); - - uint32_t lastSeqNo = 0; - for (auto it = std::begin(pageManager); it != std::end(pageManager); ++it) { - uint32_t seqNo; - CHECK(it->getSeqNumber(seqNo) == ESP_OK); - CHECK(seqNo > lastSeqNo); - } -} - -TEST_CASE("can init storage in empty flash", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - f.emu.setBounds(4, 8); - cout << "before check" << endl; - CHECK(storage.init(4, 4) == ESP_OK); - s_perf << "Time to init empty storage (4 sectors): " << f.emu.getTotalTime() << " us" << std::endl; -} - -TEST_CASE("storage doesn't add duplicates within one page", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - f.emu.setBounds(4, 8); - CHECK(storage.init(4, 4) == ESP_OK); - int bar = 0; - CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); - CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); - - Page page; - page.load(&f.part, 4); - CHECK(page.getUsedEntryCount() == 1); - CHECK(page.getErasedEntryCount() == 1); -} - -TEST_CASE("can write one item a thousand times", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - f.emu.setBounds(4, 8); - CHECK(storage.init(4, 4) == ESP_OK); - for (size_t i = 0; i < Page::ENTRY_COUNT * 4 * 2; ++i) { - REQUIRE(storage.writeItem(1, "i", static_cast(i)) == ESP_OK); - } - s_perf << "Time to write one item a thousand times: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << " " << f.emu.getWriteOps() << " " << f.emu.getReadOps() << " " << f.emu.getWriteBytes() << " " << f.emu.getReadBytes() << ")" << std::endl; -} - -TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - f.emu.setBounds(4, 8); - CHECK(storage.init(4, 4) == ESP_OK); - int bar = 0; - CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); - for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) { - CHECK(storage.writeItem(1, "foo", static_cast(++bar)) == ESP_OK); - } - CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); - - Page page; - page.load(&f.part, 4); - CHECK(page.findItem(1, itemTypeOf(), "bar") == ESP_ERR_NVS_NOT_FOUND); - page.load(&f.part, 5); - CHECK(page.findItem(1, itemTypeOf(), "bar") == ESP_OK); -} - -TEST_CASE("storage can find items on second page if first is not fully written and has cached search data", "[nvs]") -{ - PartitionEmulationFixture f(0, 3); - Storage storage(&f.part); - CHECK(storage.init(0, 3) == ESP_OK); - int bar = 0; - uint8_t bigdata[(Page::CHUNK_MAX_SIZE - Page::ENTRY_SIZE)/2] = {0}; - // write one big chunk of data - ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "1", bigdata, sizeof(bigdata))); - // write another big chunk of data - ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "2", bigdata, sizeof(bigdata))); - - // write third one; it will not fit into the first page - ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "3", bigdata, sizeof(bigdata))); - - size_t size; - ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "1", size)); - CHECK(size == sizeof(bigdata)); - ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "3", size)); - CHECK(size == sizeof(bigdata)); -} - - -TEST_CASE("can write and read variable length data lots of times", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - f.emu.setBounds(4, 8); - CHECK(storage.init(4, 4) == ESP_OK); - const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; - char buf[sizeof(str) + 16]; - size_t len = strlen(str); - for (size_t i = 0; i < Page::ENTRY_COUNT * 4 * 2; ++i) { - CAPTURE(i); - CHECK(storage.writeItem(1, ItemType::SZ, "foobaar", str, len + 1) == ESP_OK); - CHECK(storage.writeItem(1, "foo", static_cast(i)) == ESP_OK); - - uint32_t value; - CHECK(storage.readItem(1, "foo", value) == ESP_OK); - CHECK(value == i); - - fill_n(buf, sizeof(buf), 0xff); - CHECK(storage.readItem(1, ItemType::SZ, "foobaar", buf, sizeof(buf)) == ESP_OK); - CHECK(memcmp(buf, str, strlen(str) + 1) == 0); - } - s_perf << "Time to write one string and one integer a thousand times: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << " " << f.emu.getWriteOps() << " " << f.emu.getReadOps() << " " << f.emu.getWriteBytes() << " " << f.emu.getReadBytes() << ")" << std::endl; -} - - -TEST_CASE("can get length of variable length data", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - f.emu.randomize(200); - Storage storage(&f.part); - f.emu.setBounds(4, 8); - CHECK(storage.init(4, 4) == ESP_OK); - const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; - size_t len = strlen(str); - CHECK(storage.writeItem(1, ItemType::SZ, "foobaar", str, len + 1) == ESP_OK); - size_t dataSize; - CHECK(storage.getItemDataSize(1, ItemType::SZ, "foobaar", dataSize) == ESP_OK); - CHECK(dataSize == len + 1); - - CHECK(storage.writeItem(2, ItemType::BLOB, "foobaar", str, len) == ESP_OK); - CHECK(storage.getItemDataSize(2, ItemType::BLOB, "foobaar", dataSize) == ESP_OK); - CHECK(dataSize == len); -} - - -TEST_CASE("can create namespaces", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - f.emu.setBounds(4, 8); - CHECK(storage.init(4, 4) == ESP_OK); - uint8_t nsi; - CHECK(storage.createOrOpenNamespace("wifi", false, nsi) == ESP_ERR_NVS_NOT_FOUND); - - CHECK(storage.createOrOpenNamespace("wifi", true, nsi) == ESP_OK); - Page page; - page.load(&f.part, 4); - CHECK(page.findItem(Page::NS_INDEX, ItemType::U8, "wifi") == ESP_OK); -} - -TEST_CASE("storage may become full", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - f.emu.setBounds(4, 8); - CHECK(storage.init(4, 4) == ESP_OK); - for (size_t i = 0; i < Page::ENTRY_COUNT * 3; ++i) { - char name[Item::MAX_KEY_LENGTH + 1]; - snprintf(name, sizeof(name), "key%05d", static_cast(i)); - REQUIRE(storage.writeItem(1, name, static_cast(i)) == ESP_OK); - } - REQUIRE(storage.writeItem(1, "foo", 10) == ESP_ERR_NVS_NOT_ENOUGH_SPACE); -} - -TEST_CASE("can modify an item on a page which will be erased", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - CHECK(storage.init(0, 2) == ESP_OK); - for (size_t i = 0; i < Page::ENTRY_COUNT * 3 + 1; ++i) { - REQUIRE(storage.writeItem(1, "foo", 42U) == ESP_OK); - } -} - -TEST_CASE("erase operations are distributed among sectors", "[nvs]") -{ - const size_t sectors = 6; - PartitionEmulationFixture f(0, sectors); - Storage storage(&f.part); - CHECK(storage.init(0, sectors) == ESP_OK); - - /* Fill some part of storage with static values */ - const size_t static_sectors = 2; - for (size_t i = 0; i < static_sectors * Page::ENTRY_COUNT; ++i) { - char name[Item::MAX_KEY_LENGTH]; - snprintf(name, sizeof(name), "static%d", (int) i); - REQUIRE(storage.writeItem(1, name, i) == ESP_OK); - } - - /* Now perform many write operations */ - const size_t write_ops = 2000; - for (size_t i = 0; i < write_ops; ++i) { - REQUIRE(storage.writeItem(1, "value", i) == ESP_OK); - } - - /* Check that erase counts are distributed between the remaining sectors */ - const size_t max_erase_cnt = write_ops / Page::ENTRY_COUNT / (sectors - static_sectors) + 1; - for (size_t i = 0; i < sectors; ++i) { - auto erase_cnt = f.emu.getSectorEraseCount(i); - INFO("Sector " << i << " erased " << erase_cnt); - CHECK(erase_cnt <= max_erase_cnt); - } -} - -TEST_CASE("can erase items", "[nvs]") -{ - PartitionEmulationFixture f(0, 8); - Storage storage(&f.part); - CHECK(storage.init(0, 3) == ESP_OK); - for (size_t i = 0; i < Page::ENTRY_COUNT * 2 - 3; ++i) { - char name[Item::MAX_KEY_LENGTH + 1]; - snprintf(name, sizeof(name), "key%05d", static_cast(i)); - REQUIRE(storage.writeItem(3, name, static_cast(i)) == ESP_OK); - } - CHECK(storage.writeItem(1, "foo", 32) == ESP_OK); - CHECK(storage.writeItem(2, "foo", 64) == ESP_OK); - CHECK(storage.eraseItem(2, "foo") == ESP_OK); - int val; - CHECK(storage.readItem(1, "foo", val) == ESP_OK); - CHECK(val == 32); - CHECK(storage.eraseNamespace(3) == ESP_OK); - CHECK(storage.readItem(2, "foo", val) == ESP_ERR_NVS_NOT_FOUND); - CHECK(storage.readItem(3, "key00222", val) == ESP_ERR_NVS_NOT_FOUND); -} - TEST_CASE("namespace name is deep copy", "[nvs]") { char ns_name[16]; @@ -583,13 +68,14 @@ TEST_CASE("namespace name is deep copy", "[nvs]") nvs_handle_t handle_2; const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - PartitionEmulationFixture f(NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN); + + PartitionEmulationFixture f(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_open("const_name", NVS_READWRITE, &handle_1)); strcpy(ns_name, "just_kidding"); @@ -602,577 +88,6 @@ TEST_CASE("namespace name is deep copy", "[nvs]") nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME); } -TEST_CASE("readonly handle fails on writing", "[nvs]") -{ - PartitionEmulationFixture f(0, 10); - const char* str = "value 0123456789abcdef0123456789abcdef"; - const uint8_t blob[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; - - nvs_handle_t handle_1; - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - // first, creating namespace... - TEST_ESP_OK(nvs_open("ro_ns", NVS_READWRITE, &handle_1)); - nvs_close(handle_1); - - TEST_ESP_OK(nvs_open("ro_ns", NVS_READONLY, &handle_1)); - TEST_ESP_ERR(nvs_set_i32(handle_1, "key", 47), ESP_ERR_NVS_READ_ONLY); - TEST_ESP_ERR(nvs_set_str(handle_1, "key", str), ESP_ERR_NVS_READ_ONLY); - TEST_ESP_ERR(nvs_set_blob(handle_1, "key", blob, 8), ESP_ERR_NVS_READ_ONLY); - - nvs_close(handle_1); - - // without deinit it affects "nvs api tests" - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); -} - -TEST_CASE("nvs api tests", "[nvs]") -{ - PartitionEmulationFixture f(0, 10); - f.emu.randomize(100); - - nvs_handle_t handle_1; - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - - TEST_ESP_ERR(nvs_open("namespace1", NVS_READWRITE, &handle_1), ESP_ERR_NVS_NOT_INITIALIZED); - for (uint16_t i = NVS_FLASH_SECTOR; i init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); - - // TEST_ESP_ERR(nvs_set_i32(handle_1, "foo", 0x12345678), ESP_ERR_NVS_READ_ONLY); - // nvs_close(handle_1); - - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1)); - TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678)); - TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789)); - - nvs_handle_t handle_2; - TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2)); - TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a)); - const char* str = "value 0123456789abcdef0123456789abcdef"; - TEST_ESP_OK(nvs_set_str(handle_2, "key", str)); - - int32_t v1; - TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1)); - CHECK(0x23456789 == v1); - - int32_t v2; - TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2)); - CHECK(0x3456789a == v2); - - char buf[strlen(str) + 1]; - size_t buf_len = sizeof(buf); - - size_t buf_len_needed; - TEST_ESP_OK(nvs_get_str(handle_2, "key", NULL, &buf_len_needed)); - CHECK(buf_len_needed == buf_len); - - size_t buf_len_short = buf_len - 1; - TEST_ESP_ERR(ESP_ERR_NVS_INVALID_LENGTH, nvs_get_str(handle_2, "key", buf, &buf_len_short)); - CHECK(buf_len_short == buf_len); - - size_t buf_len_long = buf_len + 1; - TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len_long)); - CHECK(buf_len_long == buf_len); - - TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len)); - - CHECK(0 == strcmp(buf, str)); - nvs_close(handle_1); - nvs_close(handle_2); - - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); -} - -TEST_CASE("deinit partition doesn't affect other partition's open handles", "[nvs]") -{ - const char *OTHER_PARTITION_NAME = "other_part"; - PartitionEmulationFixture f(0, 10); - PartitionEmulationFixture f_other(0, 10, OTHER_PARTITION_NAME); - const char* str = "value 0123456789abcdef0123456789abcdef"; - const uint8_t blob[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; - - nvs_handle_t handle_1; - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - f_other.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f_other.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - TEST_ESP_OK(nvs_open_from_partition(OTHER_PARTITION_NAME, "ns", NVS_READWRITE, &handle_1)); - - // Deinitializing must not interfere with the open handle from the other partition. - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); - - TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x3456789a)); - nvs_close(handle_1); - - TEST_ESP_OK(nvs_flash_deinit_partition(OTHER_PARTITION_NAME)); -} - -TEST_CASE("nvs iterator nvs_entry_find invalid parameter test", "[nvs]") -{ - nvs_iterator_t it = reinterpret_cast(0xbeef); - CHECK(nvs_entry_find(nullptr, NULL, NVS_TYPE_ANY, &it) == ESP_ERR_INVALID_ARG); - CHECK(nvs_entry_find("nvs", NULL, NVS_TYPE_ANY, nullptr) == ESP_ERR_INVALID_ARG); -} - -TEST_CASE("nvs iterator nvs_entry_find doesn't change iterator on parameter error", "[nvs]") -{ - nvs_iterator_t it = reinterpret_cast(0xbeef); - REQUIRE(nvs_entry_find(nullptr, NULL, NVS_TYPE_ANY, &it) == ESP_ERR_INVALID_ARG); - CHECK(it == reinterpret_cast(0xbeef)); - - it = nullptr; - REQUIRE(nvs_entry_find(nullptr, NULL, NVS_TYPE_ANY, &it) == ESP_ERR_INVALID_ARG); - CHECK(it == nullptr); -} - -TEST_CASE("nvs_entry_next return ESP_ERR_INVALID_ARG on parameter is NULL", "[nvs]") -{ - CHECK(nvs_entry_next(nullptr) == ESP_ERR_INVALID_ARG); -} - -TEST_CASE("nvs_entry_info fails with ESP_ERR_INVALID_ARG if a parameter is NULL", "[nvs]") -{ - nvs_iterator_t it = reinterpret_cast(0xbeef); - nvs_entry_info_t info; - CHECK(nvs_entry_info(it, nullptr) == ESP_ERR_INVALID_ARG); - CHECK(nvs_entry_info(nullptr, &info) == ESP_ERR_INVALID_ARG); -} - -TEST_CASE("nvs_entry_info doesn't change iterator on parameter error", "[nvs]") -{ - nvs_iterator_t it = reinterpret_cast(0xbeef); - nvs_entry_info_t info; - REQUIRE(nvs_entry_info(it, nullptr) == ESP_ERR_INVALID_ARG); - CHECK(it == reinterpret_cast(0xbeef)); - - it = nullptr; - REQUIRE(nvs_entry_info(it, nullptr) == ESP_ERR_INVALID_ARG); - CHECK(it == nullptr); -} - -TEST_CASE("nvs iterators tests", "[nvs]") -{ - PartitionEmulationFixture f(0, 5); - - const uint32_t NVS_FLASH_SECTOR = 0; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { - f.emu.erase(i); - } - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - nvs_iterator_t it; - nvs_entry_info_t info; - nvs_handle handle_1; - nvs_handle handle_2; - const uint32_t blob = 0x11223344; - const char *name_1 = "namespace1"; - const char *name_2 = "namespace2"; - TEST_ESP_OK(nvs_open(name_1, NVS_READWRITE, &handle_1)); - TEST_ESP_OK(nvs_open(name_2, NVS_READWRITE, &handle_2)); - - TEST_ESP_OK(nvs_set_i8(handle_1, "value1", -11)); - TEST_ESP_OK(nvs_set_u8(handle_1, "value2", 11)); - TEST_ESP_OK(nvs_set_i16(handle_1, "value3", 1234)); - TEST_ESP_OK(nvs_set_u16(handle_1, "value4", -1234)); - TEST_ESP_OK(nvs_set_i32(handle_1, "value5", -222)); - TEST_ESP_OK(nvs_set_i32(handle_1, "value6", -222)); - TEST_ESP_OK(nvs_set_i32(handle_1, "value7", -222)); - TEST_ESP_OK(nvs_set_u32(handle_1, "value8", 222)); - TEST_ESP_OK(nvs_set_u32(handle_1, "value9", 222)); - TEST_ESP_OK(nvs_set_str(handle_1, "value10", "foo")); - TEST_ESP_OK(nvs_set_blob(handle_1, "value11", &blob, sizeof(blob))); - TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -111)); - TEST_ESP_OK(nvs_set_i32(handle_2, "value2", -111)); - TEST_ESP_OK(nvs_set_i64(handle_2, "value3", -555)); - TEST_ESP_OK(nvs_set_u64(handle_2, "value4", 555)); - - auto entry_count = [](const char *part, const char *name, nvs_type_t type)-> int { - int count = 0; - nvs_iterator_t it = nullptr; - esp_err_t res = nvs_entry_find(part, name, type, &it); - for (count = 0; res == ESP_OK; count++) { - res = nvs_entry_next(&it); - } - CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop or if no entry was found to begin with, - // res has to be ESP_ERR_NVS_NOT_FOUND or some internal error - // or programming error occurred - nvs_release_iterator(it); // unneccessary call but emphasizes the programming pattern - return count; - }; - - SECTION("No partition found return ESP_ERR_NVS_NOT_FOUND") - { - CHECK(nvs_entry_find("", NULL, NVS_TYPE_ANY, &it) == ESP_ERR_NVS_NOT_FOUND); - } - - SECTION("No matching namespace found return ESP_ERR_NVS_NOT_FOUND") - { - CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, "nonexistent", NVS_TYPE_ANY, &it) == ESP_ERR_NVS_NOT_FOUND); - } - - SECTION("nvs_entry_find sets iterator to null if no matching element found") - { - it = reinterpret_cast(0xbeef); - REQUIRE(nvs_entry_find(NVS_DEFAULT_PART_NAME, "nonexistent", NVS_TYPE_I16, &it) == ESP_ERR_NVS_NOT_FOUND); - CHECK(it == nullptr); - } - - SECTION("Finding iterator means iterator is valid") - { - it = nullptr; - CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, nullptr, NVS_TYPE_ANY, &it) == ESP_OK); - CHECK(it != nullptr); - nvs_release_iterator(it); - } - - SECTION("Return ESP_ERR_NVS_NOT_FOUND after iterating over last matching element") - { - it = nullptr; - REQUIRE(nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I16, &it) == ESP_OK); - REQUIRE(it != nullptr); - CHECK(nvs_entry_next(&it) == ESP_ERR_NVS_NOT_FOUND); - } - - SECTION("Set iterator to NULL after iterating over last matching element") - { - it = nullptr; - REQUIRE(nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I16, &it) == ESP_OK); - REQUIRE(it != nullptr); - REQUIRE(nvs_entry_next(&it) == ESP_ERR_NVS_NOT_FOUND); - CHECK(it == nullptr); - } - - SECTION("Number of entries found for specified namespace and type is correct") - { - CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_ANY) == 15); - CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY) == 11); - CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I32) == 3); - CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_I32) == 5); - CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1); - } - - SECTION("New entry is not created when existing key-value pair is set") - { - CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4); - TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -222)); - CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4); - } - - SECTION("Number of entries found decrease when entry is erased") - { - CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1); - TEST_ESP_OK(nvs_erase_key(handle_2, "value4")); - CHECK(entry_count(NVS_DEFAULT_PART_NAME, "", NVS_TYPE_U64) == 0); - } - - SECTION("All fields of nvs_entry_info_t structure are correct") - { - it = nullptr; - esp_err_t res = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I32, &it); - REQUIRE(res == ESP_OK); - string key = "value5"; - while (res == ESP_OK) { - REQUIRE(nvs_entry_info(it, &info) == ESP_OK); - - CHECK(string(name_1) == info.namespace_name); - CHECK(key == info.key); - CHECK(info.type == NVS_TYPE_I32); - - res = nvs_entry_next(&it); - key[5]++; - } - CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop, res has to be ESP_ERR_NVS_NOT_FOUND - // or some internal error or programming error occurred - CHECK(key == "value8"); - nvs_release_iterator(it); // unneccessary call but emphasizes the programming pattern - } - - SECTION("Entry info is not affected by subsequent erase") - { - nvs_entry_info_t info_after_erase; - - CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY, &it) == ESP_OK); - REQUIRE(nvs_entry_info(it, &info) == ESP_OK); - TEST_ESP_OK(nvs_erase_key(handle_1, "value1")); - REQUIRE(nvs_entry_info(it, &info_after_erase) == ESP_OK); - CHECK(memcmp(&info, &info_after_erase, sizeof(info)) == 0); - nvs_release_iterator(it); - } - - SECTION("Entry info is not affected by subsequent set") - { - nvs_entry_info_t info_after_set; - - CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY, &it) == ESP_OK); - REQUIRE(nvs_entry_info(it, &info) == ESP_OK); - TEST_ESP_OK(nvs_set_u8(handle_1, info.key, 44)); - REQUIRE(nvs_entry_info(it, &info_after_set) == ESP_OK); - CHECK(memcmp(&info, &info_after_set, sizeof(info)) == 0); - nvs_release_iterator(it); - } - - - SECTION("Iterating over multiple pages works correctly") - { - nvs_handle handle_3; - const char *name_3 = "namespace3"; - const int entries_created = 250; - - TEST_ESP_OK(nvs_open(name_3, NVS_READWRITE, &handle_3)); - for (size_t i = 0; i < entries_created; i++) { - TEST_ESP_OK(nvs_set_u8(handle_3, to_string(i).c_str(), 123)); - } - - int entries_found = 0; - it = nullptr; - esp_err_t res = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_3, NVS_TYPE_ANY, &it); - while(res == ESP_OK) { - entries_found++; - res = nvs_entry_next(&it); - } - CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop, res has to be ESP_ERR_NVS_NOT_FOUND - // or some internal error or programming error occurred - CHECK(entries_created == entries_found); - - nvs_release_iterator(it); // unneccessary call but emphasizes the programming pattern - nvs_close(handle_3); - } - - SECTION("Iterating over multi-page blob works correctly") - { - nvs_handle handle_3; - const char *name_3 = "namespace3"; - const uint8_t multipage_blob[4096 * 2] = { 0 }; - const int NUMBER_OF_ENTRIES_PER_PAGE = 125; - size_t occupied_entries; - - TEST_ESP_OK(nvs_open(name_3, NVS_READWRITE, &handle_3)); - nvs_set_blob(handle_3, "blob", multipage_blob, sizeof(multipage_blob)); - TEST_ESP_OK(nvs_get_used_entry_count(handle_3, &occupied_entries)); - CHECK(occupied_entries > NUMBER_OF_ENTRIES_PER_PAGE * 2); - - CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_3, NVS_TYPE_BLOB) == 1); - - nvs_close(handle_3); - } - - nvs_close(handle_1); - nvs_close(handle_2); - - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); -} - -TEST_CASE("Iterator with not matching type iterates correctly", "[nvs]") -{ - PartitionEmulationFixture f(0, 5); - nvs_iterator_t it; - nvs_handle_t my_handle; - const char* NAMESPACE = "test_ns_4"; - - const uint32_t NVS_FLASH_SECTOR = 0; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { - f.emu.erase(i); - } - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - // writing string to namespace (a type which spans multiple entries) - TEST_ESP_OK(nvs_open(NAMESPACE, NVS_READWRITE, &my_handle)); - TEST_ESP_OK(nvs_set_str(my_handle, "test-string", "InitString0")); - TEST_ESP_OK(nvs_commit(my_handle)); - nvs_close(my_handle); - - CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, NAMESPACE, NVS_TYPE_I32, &it) == ESP_ERR_NVS_NOT_FOUND); - - // re-init to trigger cleaning up of broken items -> a corrupted string will be erased - nvs_flash_deinit(); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - CHECK(nvs_entry_find(NVS_DEFAULT_PART_NAME, NAMESPACE, NVS_TYPE_STR, &it) == ESP_OK); - nvs_release_iterator(it); - - // without deinit it affects "nvs api tests" - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); -} - -TEST_CASE("wifi test", "[nvs]") -{ - PartitionEmulationFixture f(0, 10); - f.emu.randomize(10); - - - const uint32_t NVS_FLASH_SECTOR = 5; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - nvs_handle_t misc_handle; - TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &misc_handle)); - char log[33]; - size_t log_size = sizeof(log); - TEST_ESP_ERR(nvs_get_str(misc_handle, "log", log, &log_size), ESP_ERR_NVS_NOT_FOUND); - strcpy(log, "foobarbazfizzz"); - TEST_ESP_OK(nvs_set_str(misc_handle, "log", log)); - - nvs_handle_t net80211_handle; - TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &net80211_handle)); - - uint8_t opmode = 2; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.opmode", &opmode), ESP_ERR_NVS_NOT_FOUND); - - TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.opmode", opmode)); - - uint8_t country = 0; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.country", &opmode), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.country", opmode)); - - char ssid[36]; - size_t size = sizeof(ssid); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND); - strcpy(ssid, "my android AP"); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.ssid", ssid, size)); - - char mac[6]; - size = sizeof(mac); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND); - memset(mac, 0xab, 6); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.mac", mac, size)); - - uint8_t authmode = 1; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.authmode", authmode)); - - char pswd[65]; - size = sizeof(pswd); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pswd", pswd, &size), ESP_ERR_NVS_NOT_FOUND); - strcpy(pswd, "`123456788990-="); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pswd", pswd, size)); - - char pmk[32]; - size = sizeof(pmk); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND); - memset(pmk, 1, size); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pmk", pmk, size)); - - uint8_t chan = 1; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.chan", &chan), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.chan", chan)); - - uint8_t autoconn = 1; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "auto.conn", &autoconn), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "auto.conn", autoconn)); - - uint8_t bssid_set = 1; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bssid.set", &bssid_set), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "bssid.set", bssid_set)); - - char bssid[6]; - size = sizeof(bssid); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.bssid", bssid, &size), ESP_ERR_NVS_NOT_FOUND); - memset(mac, 0xcd, 6); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.bssid", bssid, size)); - - uint8_t phym = 3; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phym", &phym), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phym", phym)); - - uint8_t phybw = 2; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phybw", &phybw), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phybw", phybw)); - - char apsw[2]; - size = sizeof(apsw); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apsw", apsw, &size), ESP_ERR_NVS_NOT_FOUND); - memset(apsw, 0x2, size); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apsw", apsw, size)); - - char apinfo[700]; - size = sizeof(apinfo); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apinfo", apinfo, &size), ESP_ERR_NVS_NOT_FOUND); - memset(apinfo, 0, size); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apinfo", apinfo, size)); - - size = sizeof(ssid); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND); - strcpy(ssid, "ESP_A2F340"); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.ssid", ssid, size)); - - size = sizeof(mac); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND); - memset(mac, 0xac, 6); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.mac", mac, size)); - - size = sizeof(pswd); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.passwd", pswd, &size), ESP_ERR_NVS_NOT_FOUND); - strcpy(pswd, ""); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.passwd", pswd, size)); - - size = sizeof(pmk); - TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND); - memset(pmk, 1, size); - TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.pmk", pmk, size)); - - chan = 6; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.chan", &chan), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.chan", chan)); - - authmode = 0; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.authmode", authmode)); - - uint8_t hidden = 0; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.hidden", &hidden), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.hidden", hidden)); - - uint8_t max_conn = 4; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.max.conn", &max_conn), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.max.conn", max_conn)); - - uint8_t bcn_interval = 2; - TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bcn_interval", &bcn_interval), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_set_u8(net80211_handle, "bcn_interval", bcn_interval)); - - s_perf << "Time to simulate nvs init with wifi libs: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << "E " << f.emu.getWriteOps() << "W " << f.emu.getReadOps() << "R " << f.emu.getWriteBytes() << "Wb " << f.emu.getReadBytes() << "Rb)" << std::endl; - - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); -} - TEST_CASE("writing the identical content does not write or erase", "[nvs]") { PartitionEmulationFixture f(0, 20); @@ -1180,9 +95,9 @@ TEST_CASE("writing the identical content does not write or erase", "[nvs]") const uint32_t NVS_FLASH_SECTOR = 5; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 10; f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); nvs_handle misc_handle; TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &misc_handle)); @@ -1213,7 +128,7 @@ TEST_CASE("writing the identical content does not write or erase", "[nvs]") CHECK(f.emu.getReadOps() != 0); // Test writing a multi-page blob, then changing it - uint8_t blob[Page::CHUNK_MAX_SIZE * 3] = {0}; + uint8_t blob[nvs::Page::CHUNK_MAX_SIZE * 3] = {0}; memset(blob, 1, sizeof(blob)); nvs_set_blob(misc_handle, "test_blob", blob, sizeof(blob)); f.emu.clearStats(); @@ -1239,9 +154,9 @@ TEST_CASE("can init storage from flash with random contents", "[nvs]") const uint32_t NVS_FLASH_SECTOR = 5; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &handle)); @@ -1253,7 +168,6 @@ TEST_CASE("can init storage from flash with random contents", "[nvs]") TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } - TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]") { const size_t testIters = 3000; @@ -1271,9 +185,9 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]") const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); nvs_handle_t handle_1; TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); @@ -1285,7 +199,7 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]") TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2)); TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789 % (i + 1))); TEST_ESP_OK(nvs_set_i32(handle_2, "foo", static_cast(i))); - const char* str = "value 0123456789abcdef0123456789abcdef %09d"; + const char *str = "value 0123456789abcdef0123456789abcdef %09d"; char str_buf[128]; snprintf(str_buf, sizeof(str_buf), str, i + count * 1024); TEST_ESP_OK(nvs_set_str(handle_2, "key", str_buf)); @@ -1311,6 +225,7 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]") TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } } + extern "C" void nvs_dump(const char *partName); class RandomTest { @@ -1319,8 +234,8 @@ class RandomTest { int32_t v1 = 0, v2 = 0; uint64_t v3 = 0, v4 = 0; static const size_t strBufLen = 1024; - static const size_t smallBlobLen = Page::CHUNK_MAX_SIZE / 3; - static const size_t largeBlobLen = Page::CHUNK_MAX_SIZE * 3; + static const size_t smallBlobLen = nvs::Page::CHUNK_MAX_SIZE / 3; + static const size_t largeBlobLen = nvs::Page::CHUNK_MAX_SIZE * 3; char v5[strBufLen], v6[strBufLen], v7[strBufLen], v8[strBufLen], v9[strBufLen]; uint8_t v10[smallBlobLen], v11[largeBlobLen]; bool written[nKeys]; @@ -1332,204 +247,195 @@ public: } template - esp_err_t doRandomThings(nvs_handle_t handle, TGen gen, size_t& count) { + esp_err_t doRandomThings(nvs_handle_t handle, TGen gen, size_t &count) + { - const char* keys[] = {"foo", "bar", "longkey_0123456", "another key", "param1", "param2", "param3", "param4", "param5", "singlepage", "multipage"}; - const ItemType types[] = {ItemType::I32, ItemType::I32, ItemType::U64, ItemType::U64, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::BLOB, ItemType::BLOB}; + const char *keys[] = {"foo", "bar", "longkey_0123456", "another key", "param1", "param2", "param3", "param4", "param5", "singlepage", "multipage"}; + const nvs::ItemType types[] = {nvs::ItemType::I32, nvs::ItemType::I32, nvs::ItemType::U64, nvs::ItemType::U64, nvs::ItemType::SZ, nvs::ItemType::SZ, nvs::ItemType::SZ, nvs::ItemType::SZ, nvs::ItemType::SZ, nvs::ItemType::BLOB, nvs::ItemType::BLOB}; - void* values[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9, &v10, &v11}; + void *values[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9, &v10, &v11}; const size_t nKeys = sizeof(keys) / sizeof(keys[0]); static_assert(nKeys == sizeof(types) / sizeof(types[0]), ""); static_assert(nKeys == sizeof(values) / sizeof(values[0]), ""); auto randomRead = [&](size_t index) -> esp_err_t { - switch (types[index]) { - case ItemType::I32: - { - int32_t val; - auto err = nvs_get_i32(handle, keys[index], &val); - if (err == ESP_ERR_FLASH_OP_FAIL) { - return err; - } - if (!written[index]) { - REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); - } - else { - REQUIRE(err == ESP_OK); - REQUIRE(val == *reinterpret_cast(values[index])); - } - break; + switch (types[index]) + { + case nvs::ItemType::I32: { + int32_t val; + auto err = nvs_get_i32(handle, keys[index], &val); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; } - - case ItemType::U64: - { - uint64_t val; - auto err = nvs_get_u64(handle, keys[index], &val); - if (err == ESP_ERR_FLASH_OP_FAIL) { - return err; - } - if (!written[index]) { - REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); - } - else { - REQUIRE(err == ESP_OK); - REQUIRE(val == *reinterpret_cast(values[index])); - } - break; + if (!written[index]) { + REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); + } else { + REQUIRE(err == ESP_OK); + REQUIRE(val == *reinterpret_cast(values[index])); } + break; + } - case ItemType::SZ: - { - char buf[strBufLen]; - size_t len = strBufLen; - auto err = nvs_get_str(handle, keys[index], buf, &len); - if (err == ESP_ERR_FLASH_OP_FAIL) { - return err; - } - if (!written[index]) { - REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); - } - else { - REQUIRE(err == ESP_OK); - REQUIRE(strncmp(buf, reinterpret_cast(values[index]), strBufLen) == 0); - } - break; + case nvs::ItemType::U64: { + uint64_t val; + auto err = nvs_get_u64(handle, keys[index], &val); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; } - - case ItemType::BLOB: - { - uint32_t blobBufLen = 0; - if(strncmp(keys[index],"singlepage", sizeof("singlepage")) == 0) { - blobBufLen = smallBlobLen ; - } else { - blobBufLen = largeBlobLen ; - - } - uint8_t buf[blobBufLen]; - memset(buf, 0, blobBufLen); - - size_t len = blobBufLen; - auto err = nvs_get_blob(handle, keys[index], buf, &len); - if (err == ESP_ERR_FLASH_OP_FAIL) { - return err; - } - if (!written[index]) { - REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); - } - else { - REQUIRE(err == ESP_OK); - REQUIRE(memcmp(buf, reinterpret_cast(values[index]), blobBufLen) == 0); - } - break; + if (!written[index]) { + REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); + } else { + REQUIRE(err == ESP_OK); + REQUIRE(val == *reinterpret_cast(values[index])); } + break; + } + + case nvs::ItemType::SZ: { + char buf[strBufLen]; + size_t len = strBufLen; + auto err = nvs_get_str(handle, keys[index], buf, &len); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (!written[index]) { + REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); + } else { + REQUIRE(err == ESP_OK); + REQUIRE(strncmp(buf, reinterpret_cast(values[index]), strBufLen) == 0); + } + break; + } + + case nvs::ItemType::BLOB: { + uint32_t blobBufLen = 0; + if (strncmp(keys[index], "singlepage", sizeof("singlepage")) == 0) { + blobBufLen = smallBlobLen ; + } else { + blobBufLen = largeBlobLen ; + + } + uint8_t buf[blobBufLen]; + memset(buf, 0, blobBufLen); + + size_t len = blobBufLen; + auto err = nvs_get_blob(handle, keys[index], buf, &len); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (!written[index]) { + REQUIRE(err == ESP_ERR_NVS_NOT_FOUND); + } else { + REQUIRE(err == ESP_OK); + REQUIRE(memcmp(buf, reinterpret_cast(values[index]), blobBufLen) == 0); + } + break; + } - default: - assert(0); + default: + assert(0); } return ESP_OK; }; auto randomWrite = [&](size_t index) -> esp_err_t { - switch (types[index]) { - case ItemType::I32: - { - int32_t val = static_cast(gen()); + switch (types[index]) + { + case nvs::ItemType::I32: { + int32_t val = static_cast(gen()); - auto err = nvs_set_i32(handle, keys[index], val); - if (err == ESP_ERR_FLASH_OP_FAIL) { - return err; - } - if (err == ESP_ERR_NVS_REMOVE_FAILED) { - written[index] = true; - *reinterpret_cast(values[index]) = val; - return ESP_ERR_FLASH_OP_FAIL; - } - REQUIRE(err == ESP_OK); - written[index] = true; - *reinterpret_cast(values[index]) = val; - break; + auto err = nvs_set_i32(handle, keys[index], val); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; } - - case ItemType::U64: - { - uint64_t val = static_cast(gen()); - - auto err = nvs_set_u64(handle, keys[index], val); - if (err == ESP_ERR_FLASH_OP_FAIL) { - return err; - } - if (err == ESP_ERR_NVS_REMOVE_FAILED) { - written[index] = true; - *reinterpret_cast(values[index]) = val; - return ESP_ERR_FLASH_OP_FAIL; - } - REQUIRE(err == ESP_OK); + if (err == ESP_ERR_NVS_REMOVE_FAILED) { written[index] = true; - *reinterpret_cast(values[index]) = val; - break; + *reinterpret_cast(values[index]) = val; + return ESP_ERR_FLASH_OP_FAIL; } + REQUIRE(err == ESP_OK); + written[index] = true; + *reinterpret_cast(values[index]) = val; + break; + } - case ItemType::SZ: - { - char buf[strBufLen]; - size_t len = strBufLen; + case nvs::ItemType::U64: { + uint64_t val = static_cast(gen()); - size_t strLen = gen() % (strBufLen - 1); - std::generate_n(buf, strLen, [&]() -> char { - const char c = static_cast(gen() % 127); - return (c < 32) ? 32 : c; - }); - buf[strLen] = 0; - - auto err = nvs_set_str(handle, keys[index], buf); - if (err == ESP_ERR_FLASH_OP_FAIL) { - return err; - } - if (err == ESP_ERR_NVS_REMOVE_FAILED) { - written[index] = true; - strncpy(reinterpret_cast(values[index]), buf, strBufLen); - return ESP_ERR_FLASH_OP_FAIL; - } - REQUIRE(err == ESP_OK); + auto err = nvs_set_u64(handle, keys[index], val); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (err == ESP_ERR_NVS_REMOVE_FAILED) { written[index] = true; - strncpy(reinterpret_cast(values[index]), buf, strBufLen); - break; + *reinterpret_cast(values[index]) = val; + return ESP_ERR_FLASH_OP_FAIL; } + REQUIRE(err == ESP_OK); + written[index] = true; + *reinterpret_cast(values[index]) = val; + break; + } - case ItemType::BLOB: - { - uint32_t blobBufLen = 0; - if(strncmp(keys[index],"singlepage", sizeof("singlepage")) == 0) { - blobBufLen = smallBlobLen ; - } else { - blobBufLen = largeBlobLen ; - } - uint8_t buf[blobBufLen]; - memset(buf, 0, blobBufLen); - size_t blobLen = gen() % blobBufLen; - std::generate_n(buf, blobLen, [&]() -> uint8_t { - return static_cast(gen() % 256); - }); + case nvs::ItemType::SZ: { + char buf[strBufLen]; + size_t len = strBufLen; - auto err = nvs_set_blob(handle, keys[index], buf, blobLen); - if (err == ESP_ERR_FLASH_OP_FAIL) { - return err; - } - if (err == ESP_ERR_NVS_REMOVE_FAILED) { - written[index] = true; - memcpy(reinterpret_cast(values[index]), buf, blobBufLen); - return ESP_ERR_FLASH_OP_FAIL; - } - REQUIRE(err == ESP_OK); + size_t strLen = gen() % (strBufLen - 1); + std::generate_n(buf, strLen, [&]() -> char { + const char c = static_cast(gen() % 127); + return (c < 32) ? 32 : c; + }); + buf[strLen] = 0; + + auto err = nvs_set_str(handle, keys[index], buf); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (err == ESP_ERR_NVS_REMOVE_FAILED) { written[index] = true; - memcpy(reinterpret_cast(values[index]), buf, blobBufLen); - break; + strncpy(reinterpret_cast(values[index]), buf, strBufLen); + return ESP_ERR_FLASH_OP_FAIL; } + REQUIRE(err == ESP_OK); + written[index] = true; + strncpy(reinterpret_cast(values[index]), buf, strBufLen); + break; + } - default: - assert(0); + case nvs::ItemType::BLOB: { + uint32_t blobBufLen = 0; + if (strncmp(keys[index], "singlepage", sizeof("singlepage")) == 0) { + blobBufLen = smallBlobLen ; + } else { + blobBufLen = largeBlobLen ; + } + uint8_t buf[blobBufLen]; + memset(buf, 0, blobBufLen); + size_t blobLen = gen() % blobBufLen; + std::generate_n(buf, blobLen, [&]() -> uint8_t { + return static_cast(gen() % 256); + }); + + auto err = nvs_set_blob(handle, keys[index], buf, blobLen); + if (err == ESP_ERR_FLASH_OP_FAIL) { + return err; + } + if (err == ESP_ERR_NVS_REMOVE_FAILED) { + written[index] = true; + memcpy(reinterpret_cast(values[index]), buf, blobBufLen); + return ESP_ERR_FLASH_OP_FAIL; + } + REQUIRE(err == ESP_OK); + written[index] = true; + memcpy(reinterpret_cast(values[index]), buf, blobBufLen); + break; + } + + default: + assert(0); } return ESP_OK; }; @@ -1538,25 +444,26 @@ public: for (; count != 0; --count) { size_t index = gen() % (nKeys); switch (gen() % 3) { - case 0: // read, 1/3 - if (randomRead(index) == ESP_ERR_FLASH_OP_FAIL) { - return ESP_ERR_FLASH_OP_FAIL; - } - break; + case 0: // read, 1/3 + if (randomRead(index) == ESP_ERR_FLASH_OP_FAIL) { + return ESP_ERR_FLASH_OP_FAIL; + } + break; - default: // write, 2/3 - if (randomWrite(index) == ESP_ERR_FLASH_OP_FAIL) { - return ESP_ERR_FLASH_OP_FAIL; - } - break; + default: // write, 2/3 + if (randomWrite(index) == ESP_ERR_FLASH_OP_FAIL) { + return ESP_ERR_FLASH_OP_FAIL; + } + break; } } return ESP_OK; } - esp_err_t handleExternalWriteAtIndex(uint8_t index, const void* value, const size_t len ) { - if(index == 9) { /* This is only done for small-page blobs for now*/ - if(len > smallBlobLen) { + esp_err_t handleExternalWriteAtIndex(uint8_t index, const void *value, const size_t len ) + { + if (index == 9) { /* This is only done for small-page blobs for now*/ + if (len > smallBlobLen) { return ESP_FAIL; } memcpy(v10, value, len); @@ -1568,36 +475,6 @@ public: } }; -TEST_CASE("monkey test", "[nvs][monkey]") -{ - std::random_device rd; - std::mt19937 gen(rd()); - uint32_t seed = 3; - gen.seed(seed); - - PartitionEmulationFixture f(0, 10); - f.emu.randomize(seed); - f.emu.clearStats(); - - const uint32_t NVS_FLASH_SECTOR = 2; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - nvs_handle_t handle; - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); - RandomTest test; - size_t count = 1000; - CHECK(test.doRandomThings(handle, gen, count) == ESP_OK); - - s_perf << "Monkey test: nErase=" << f.emu.getEraseOps() << " nWrite=" << f.emu.getWriteOps() << std::endl; - - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); -} - TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") { std::random_device rd; @@ -1633,7 +510,7 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") nvs_handle_t handle; size_t count = iter_count; - if (NVSPartitionManager::get_instance()->init_custom(&f.part, + if (nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK) { auto res = ESP_ERR_FLASH_OP_FAIL; @@ -1650,9 +527,9 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") } } - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); auto res = test.doRandomThings(handle, gen, count); if (res != ESP_OK) { @@ -1666,28 +543,6 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") } } -TEST_CASE("test for memory leaks in open/set", "[leaks]") -{ - PartitionEmulationFixture f(0, 10); - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - for (int i = 0; i < 100000; ++i) { - nvs_handle_t light_handle = 0; - char lightbulb[1024] = {12, 13, 14, 15, 16}; - TEST_ESP_OK(nvs_open("light", NVS_READWRITE, &light_handle)); - TEST_ESP_OK(nvs_set_blob(light_handle, "key", lightbulb, sizeof(lightbulb))); - TEST_ESP_OK(nvs_commit(light_handle)); - nvs_close(light_handle); - } - - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); -} - TEST_CASE("duplicate items are removed", "[nvs][dupes]") { PartitionEmulationFixture f(0, 3); @@ -1699,11 +554,11 @@ TEST_CASE("duplicate items are removed", "[nvs][dupes]") } { // add another two without deleting the first one - nvs::Item item(1, ItemType::U8, 1, "opmode"); + nvs::Item item(1, nvs::ItemType::U8, 1, "opmode"); item.data[0] = 2; item.crc32 = item.calculateCrc32(); - f.emu.write(3 * 32, reinterpret_cast(&item), sizeof(item)); - f.emu.write(4 * 32, reinterpret_cast(&item), sizeof(item)); + f.emu.write(3 * 32, reinterpret_cast(&item), sizeof(item)); + f.emu.write(4 * 32, reinterpret_cast(&item), sizeof(item)); uint32_t mask = 0xFFFFFFEA; f.emu.write(32, &mask, 4); } @@ -1716,7 +571,7 @@ TEST_CASE("duplicate items are removed", "[nvs][dupes]") CHECK(val == 2); } { - Page p; + nvs::Page p; p.load(&f.part, 0); CHECK(p.getErasedEntryCount() == 2); CHECK(p.getUsedEntryCount() == 1); @@ -1728,36 +583,36 @@ TEST_CASE("recovery after failure to write data", "[nvs]") PartitionEmulationFixture f(0, 3); const char str[] = "value 0123456789abcdef012345678value 0123456789abcdef012345678"; - // make flash write fail exactly in Page::writeEntryData + // make flash write fail exactly in nvs::Page::writeEntryData f.emu.failAfter(17); { - Storage storage(&f.part); + nvs::Storage storage(&f.part); TEST_ESP_OK(storage.init(0, 3)); - TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_FLASH_OP_FAIL); + TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::SZ, "key", str, strlen(str)), ESP_ERR_FLASH_OP_FAIL); // check that repeated operations cause an error - TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_NVS_INVALID_STATE); + TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::SZ, "key", str, strlen(str)), ESP_ERR_NVS_INVALID_STATE); uint8_t val; - TEST_ESP_ERR(storage.readItem(1, ItemType::U8, "key", &val, sizeof(val)), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(storage.readItem(1, nvs::ItemType::U8, "key", &val, sizeof(val)), ESP_ERR_NVS_NOT_FOUND); } { // load page and check that data was erased - Page p; + nvs::Page p; p.load(&f.part, 0); CHECK(p.getErasedEntryCount() == 3); CHECK(p.getUsedEntryCount() == 0); // try to write again - TEST_ESP_OK(p.writeItem(1, ItemType::SZ, "key", str, strlen(str))); + TEST_ESP_OK(p.writeItem(1, nvs::ItemType::SZ, "key", str, strlen(str))); } } TEST_CASE("crc errors in item header are handled", "[nvs]") { PartitionEmulationFixture f(0, 3); - Storage storage(&f.part); + nvs::Storage storage(&f.part); // prepare some data TEST_ESP_OK(storage.init(0, 3)); TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast(1))); @@ -1776,8 +631,8 @@ TEST_CASE("crc errors in item header are handled", "[nvs]") TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, storage.readItem(1, "value1", val)); // 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]; + for (size_t i = 0; i < nvs::Page::ENTRY_COUNT; ++i) { + char item_name[nvs::Item::MAX_KEY_LENGTH + 1]; snprintf(item_name, sizeof(item_name), "item_%ld", (long int)i); TEST_ESP_OK(storage.writeItem(1, item_name, static_cast(i))); } @@ -1799,11 +654,11 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]") const uint64_t after_val = 0xaf7e4; // write some data { - Page p; + nvs::Page p; p.load(&f.part, 0); TEST_ESP_OK(p.writeItem(0, "before", before_val)); - const char* str = "foobar"; - TEST_ESP_OK(p.writeItem(0, ItemType::SZ, "key", str, strlen(str))); + const char *str = "foobar"; + TEST_ESP_OK(p.writeItem(0, nvs::ItemType::SZ, "key", str, strlen(str))); TEST_ESP_OK(p.writeItem(0, "after", after_val)); } // corrupt some data @@ -1813,7 +668,7 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]") CHECK(f.emu.write(32 * 3 + 8, &w, sizeof(w))); // load and check { - Page p; + nvs::Page p; p.load(&f.part, 0); CHECK(p.getUsedEntryCount() == 2); CHECK(p.getErasedEntryCount() == 2); @@ -1821,80 +676,20 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]") uint64_t val; TEST_ESP_OK(p.readItem(0, "before", val)); CHECK(val == before_val); - TEST_ESP_ERR(p.findItem(0, ItemType::SZ, "key"), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(p.findItem(0, nvs::ItemType::SZ, "key"), ESP_ERR_NVS_NOT_FOUND); TEST_ESP_OK(p.readItem(0, "after", val)); CHECK(val == after_val); } } - -TEST_CASE("read/write failure (TW8406)", "[nvs]") -{ - PartitionEmulationFixture f(0, 3); - NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3); - for (int attempts = 0; attempts < 3; ++attempts) { - int i = 0; - nvs_handle_t light_handle = 0; - char key[15] = {0}; - char data[76] = {12, 13, 14, 15, 16}; - uint8_t number = 20; - size_t data_len = sizeof(data); - - ESP_ERROR_CHECK(nvs_open("LIGHT", NVS_READWRITE, &light_handle)); - ESP_ERROR_CHECK(nvs_set_u8(light_handle, "RecordNum", number)); - for (i = 0; i < number; ++i) { - sprintf(key, "light%d", i); - ESP_ERROR_CHECK(nvs_set_blob(light_handle, key, data, sizeof(data))); - } - nvs_commit(light_handle); - - uint8_t get_number = 0; - ESP_ERROR_CHECK(nvs_get_u8(light_handle, "RecordNum", &get_number)); - REQUIRE(number == get_number); - for (i = 0; i < number; ++i) { - char data[76] = {0}; - sprintf(key, "light%d", i); - ESP_ERROR_CHECK(nvs_get_blob(light_handle, key, data, &data_len)); - } - nvs_close(light_handle); - } - - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); -} - -TEST_CASE("nvs_flash_init checks for an empty page", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE; - uint8_t blob[blob_size] = {0}; - PartitionEmulationFixture f(0, 8); - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) ); - nvs_handle_t handle; - TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); - // Fill first page - TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size) ); - // Fill second page - TEST_ESP_OK( nvs_set_blob(handle, "2a", blob, blob_size) ); - // Fill third page - TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, blob_size) ); - TEST_ESP_OK( nvs_commit(handle) ); - nvs_close(handle); - TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); - // first two pages are now full, third one is writable, last two are empty - // init should fail - TEST_ESP_ERR( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3), - ESP_ERR_NVS_NO_FREE_PAGES ); - - // in case this test fails, to not affect other tests - nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME); -} - TEST_CASE("multiple partitions access check", "[nvs]") { SpiFlashEmulator emu(10); PartitionEmulation p0(&emu, 0 * SPI_FLASH_SEC_SIZE, 5 * SPI_FLASH_SEC_SIZE, "nvs1"); PartitionEmulation p1(&emu, 5 * SPI_FLASH_SEC_SIZE, 5 * SPI_FLASH_SEC_SIZE, "nvs2"); - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&p0, 0, 5) ); - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&p1, 5, 5) ); + + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(&p0, 0, 5) ); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(&p1, 5, 5) ); nvs_handle_t handle1, handle2; TEST_ESP_OK( nvs_open_from_partition("nvs1", "test", NVS_READWRITE, &handle1) ); TEST_ESP_OK( nvs_open_from_partition("nvs2", "test", NVS_READWRITE, &handle2) ); @@ -1910,185 +705,25 @@ TEST_CASE("multiple partitions access check", "[nvs]") TEST_ESP_OK(nvs_flash_deinit_partition(p1.get_partition_name())); } -TEST_CASE("nvs page selection takes into account free entries also not just erased entries", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE/2; - uint8_t blob[blob_size] = {0}; - PartitionEmulationFixture f(0, 3); - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) ); - nvs_handle_t handle; - TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); - // Fill first page - TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size/3) ); - TEST_ESP_OK( nvs_set_blob(handle, "1b", blob, blob_size) ); - // Fill second page - TEST_ESP_OK( nvs_set_blob(handle, "2a", blob, blob_size) ); - TEST_ESP_OK( nvs_set_blob(handle, "2b", blob, blob_size) ); - - // The item below should be able to fit the first page. - TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, 4) ); - TEST_ESP_OK( nvs_commit(handle) ); - nvs_close(handle); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("calculate used and free space", "[nvs]") -{ - PartitionEmulationFixture f(0, 6); - nvs_flash_deinit(); - 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_t 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(NVSPartitionManager::get_instance()->init_custom(&f.part, 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_t 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_t 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_t 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 + 4 == stat2.free_entries); - CHECK(stat1.namespace_count == 3); - CHECK(stat1.total_entries == stat2.total_entries); - CHECK(stat1.used_entries == 12); - - // 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 == 4); - - CHECK(stat1.used_entries == (h1_count_entries + h2_count_entries + h3_count_entries + stat1.namespace_count)); - - nvs_close(handle_3); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -// TODO: leaks memory +// leaks memory TEST_CASE("Recovery from power-off when the entry being erased is not on active page", "[nvs]") { - const size_t blob_size = Page::CHUNK_MAX_SIZE/2 ; + const size_t blob_size = nvs::Page::CHUNK_MAX_SIZE / 2 ; size_t read_size = blob_size; uint8_t blob[blob_size] = {0x11}; PartitionEmulationFixture f(0, 3); - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) ); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) ); nvs_handle_t handle; TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); f.emu.clearStats(); - f.emu.failAfter(Page::CHUNK_MAX_SIZE/4 + 75); + f.emu.failAfter(nvs::Page::CHUNK_MAX_SIZE / 4 + 75); TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size) ); TEST_ESP_OK( nvs_set_blob(handle, "1b", blob, blob_size) ); TEST_ESP_ERR( nvs_erase_key(handle, "1a"), ESP_ERR_FLASH_OP_FAIL ); - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) ); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) ); /* Check 1a is erased fully*/ TEST_ESP_ERR( nvs_get_blob(handle, "1a", blob, &read_size), ESP_ERR_NVS_NOT_FOUND); @@ -2101,41 +736,41 @@ TEST_CASE("Recovery from power-off when the entry being erased is not on active TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } -// TODO: leaks memory +// leaks memory TEST_CASE("Recovery from power-off when page is being freed.", "[nvs]") { - const size_t blob_size = (Page::ENTRY_COUNT-3) * Page::ENTRY_SIZE; - size_t read_size = blob_size/2; + const size_t blob_size = (nvs::Page::ENTRY_COUNT - 3) * nvs::Page::ENTRY_SIZE; + size_t read_size = blob_size / 2; uint8_t blob[blob_size] = {0}; PartitionEmulationFixture f(0, 3); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); nvs_handle_t handle; TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle)); // Fill first page - TEST_ESP_OK(nvs_set_blob(handle, "1a", blob, blob_size/3)); - TEST_ESP_OK(nvs_set_blob(handle, "1b", blob, blob_size/3)); - TEST_ESP_OK(nvs_set_blob(handle, "1c", blob, blob_size/4)); + TEST_ESP_OK(nvs_set_blob(handle, "1a", blob, blob_size / 3)); + TEST_ESP_OK(nvs_set_blob(handle, "1b", blob, blob_size / 3)); + TEST_ESP_OK(nvs_set_blob(handle, "1c", blob, blob_size / 4)); // Fill second page - TEST_ESP_OK(nvs_set_blob(handle, "2a", blob, blob_size/2)); - TEST_ESP_OK(nvs_set_blob(handle, "2b", blob, blob_size/2)); + TEST_ESP_OK(nvs_set_blob(handle, "2a", blob, blob_size / 2)); + TEST_ESP_OK(nvs_set_blob(handle, "2b", blob, blob_size / 2)); TEST_ESP_OK(nvs_erase_key(handle, "1c")); f.emu.clearStats(); - f.emu.failAfter(6 * Page::ENTRY_COUNT); - TEST_ESP_ERR(nvs_set_blob(handle, "1d", blob, blob_size/4), ESP_ERR_FLASH_OP_FAIL); + f.emu.failAfter(6 * nvs::Page::ENTRY_COUNT); + TEST_ESP_ERR(nvs_set_blob(handle, "1d", blob, blob_size / 4), ESP_ERR_FLASH_OP_FAIL); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); - read_size = blob_size/3; + read_size = blob_size / 3; TEST_ESP_OK( nvs_get_blob(handle, "1a", blob, &read_size)); TEST_ESP_OK( nvs_get_blob(handle, "1b", blob, &read_size)); - read_size = blob_size /4; + read_size = blob_size / 4; TEST_ESP_ERR( nvs_get_blob(handle, "1c", blob, &read_size), ESP_ERR_NVS_NOT_FOUND); TEST_ESP_ERR( nvs_get_blob(handle, "1d", blob, &read_size), ESP_ERR_NVS_NOT_FOUND); - read_size = blob_size /2; + read_size = blob_size / 2; TEST_ESP_OK( nvs_get_blob(handle, "2a", blob, &read_size)); TEST_ESP_OK( nvs_get_blob(handle, "2b", blob, &read_size)); @@ -2145,248 +780,13 @@ TEST_CASE("Recovery from power-off when page is being freed.", "[nvs]") TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } -TEST_CASE("Multi-page blobs are supported", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE *2; - uint8_t blob[blob_size] = {0}; - PartitionEmulationFixture f(0, 5); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5)); - nvs_handle_t handle; - TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle)); - TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); - TEST_ESP_OK(nvs_commit(handle)); - nvs_close(handle); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("Failures are handled while storing multi-page blobs", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE *7; - uint8_t blob[blob_size] = {0}; - PartitionEmulationFixture f(0, 5); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5)); - nvs_handle_t handle; - TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle)); - TEST_ESP_ERR(nvs_set_blob(handle, "abc", blob, blob_size), ESP_ERR_NVS_VALUE_TOO_LONG); - TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE*2)); - TEST_ESP_OK(nvs_commit(handle)); - nvs_close(handle); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("Reading multi-page blobs", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE *3; - uint8_t blob[blob_size]; - uint8_t blob_read[blob_size]; - size_t read_size = blob_size; - PartitionEmulationFixture f(0, 5); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5)); - nvs_handle_t handle; - memset(blob, 0x11, blob_size); - memset(blob_read, 0xee, blob_size); - TEST_ESP_OK(nvs_open("readTest", NVS_READWRITE, &handle)); - TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); - TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size)); - CHECK(memcmp(blob, blob_read, blob_size) == 0); - TEST_ESP_OK(nvs_commit(handle)); - nvs_close(handle); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("Modification of values for Multi-page blobs are supported", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE *2; - uint8_t blob[blob_size] = {0}; - uint8_t blob_read[blob_size] = {0xfe};; - uint8_t blob2[blob_size] = {0x11}; - uint8_t blob3[blob_size] = {0x22}; - uint8_t blob4[blob_size] ={ 0x33}; - size_t read_size = blob_size; - PartitionEmulationFixture f(0, 6); - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 6) ); - nvs_handle_t handle; - memset(blob, 0x11, blob_size); - memset(blob2, 0x22, blob_size); - memset(blob3, 0x33, blob_size); - memset(blob4, 0x44, blob_size); - memset(blob_read, 0xff, blob_size); - TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); - TEST_ESP_OK( nvs_set_blob(handle, "abc", blob, blob_size) ); - TEST_ESP_OK( nvs_set_blob(handle, "abc", blob2, blob_size) ); - TEST_ESP_OK( nvs_set_blob(handle, "abc", blob3, blob_size) ); - TEST_ESP_OK( nvs_set_blob(handle, "abc", blob4, blob_size) ); - TEST_ESP_OK( nvs_get_blob(handle, "abc", blob_read, &read_size)); - CHECK(memcmp(blob4, blob_read, blob_size) == 0); - TEST_ESP_OK( nvs_commit(handle) ); - nvs_close(handle); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("Modification from single page blob to multi-page", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE *3; - uint8_t blob[blob_size] = {0}; - uint8_t blob_read[blob_size] = {0xff}; - size_t read_size = blob_size; - PartitionEmulationFixture f(0, 5); - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) ); - nvs_handle_t handle; - TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle) ); - TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE/2)); - TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); - TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size)); - CHECK(memcmp(blob, blob_read, blob_size) == 0); - TEST_ESP_OK(nvs_commit(handle) ); - nvs_close(handle); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("Modification from multi-page to single page", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE *3; - uint8_t blob[blob_size] = {0}; - uint8_t blob_read[blob_size] = {0xff}; - size_t read_size = blob_size; - PartitionEmulationFixture f(0, 5); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) ); - nvs_handle_t handle; - TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle) ); - TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); - TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE/2)); - TEST_ESP_OK(nvs_set_blob(handle, "abc2", blob, blob_size)); - TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size)); - CHECK(memcmp(blob, blob_read, Page::CHUNK_MAX_SIZE) == 0); - TEST_ESP_OK(nvs_commit(handle) ); - nvs_close(handle); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("Multi-page blob erased using nvs_erase_key should not be found when probed for just length", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE *3; - uint8_t blob[blob_size] = {0}; - size_t read_size = blob_size; - PartitionEmulationFixture f(0, 5); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5)); - nvs_handle handle; - TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle)); - TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); - TEST_ESP_OK(nvs_erase_key(handle, "abc")); - TEST_ESP_ERR(nvs_get_blob(handle, "abc", NULL, &read_size), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_commit(handle)); - nvs_close(handle); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - - -TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE *3 ; - uint8_t blob[blob_size] = {0x11}; - uint8_t blob2[blob_size] = {0x22}; - uint8_t blob3[blob_size] = {0x33}; - PartitionEmulationFixture f(0, 5); - Storage storage(&f.part); - - TEST_ESP_OK(storage.init(0, 5)); - - TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, "key", blob, sizeof(blob))); - - - TEST_ESP_OK(storage.init(0, 5)); - /* Check that multi-page item is still available.**/ - TEST_ESP_OK(storage.readItem(1, ItemType::BLOB, "key", blob, sizeof(blob))); - - TEST_ESP_ERR(storage.writeItem(1, ItemType::BLOB, "key2", blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE); - - Page p; - p.load(&f.part, 3); // This is where index will be placed. - p.erase(); - - TEST_ESP_OK(storage.init(0, 5)); - - TEST_ESP_ERR(storage.readItem(1, ItemType::BLOB, "key", blob, sizeof(blob)), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, "key3", blob, sizeof(blob))); -} - -TEST_CASE("nvs blob fragmentation test", "[nvs]") -{ - PartitionEmulationFixture f(0, 4); - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 4) ); - const size_t BLOB_SIZE = 3500; - uint8_t *blob = (uint8_t*) malloc(BLOB_SIZE); - CHECK(blob != NULL); - memset(blob, 0xEE, BLOB_SIZE); - const uint32_t magic = 0xff33eaeb; - nvs_handle_t h; - TEST_ESP_OK( nvs_open("blob_tests", NVS_READWRITE, &h) ); - for (int i = 0; i < 128; i++) { - INFO("Iteration " << i << "...\n"); - TEST_ESP_OK( nvs_set_u32(h, "magic", magic) ); - TEST_ESP_OK( nvs_set_blob(h, "blob", blob, BLOB_SIZE) ); - char seq_buf[16]; - sprintf(seq_buf, "seq%d", i); - TEST_ESP_OK( nvs_set_u32(h, seq_buf, i) ); - } - free(blob); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("nvs code handles errors properly when partition is near to full", "[nvs]") -{ - const size_t blob_size = Page::CHUNK_MAX_SIZE * 0.3 ; - uint8_t blob[blob_size] = {0x11}; - PartitionEmulationFixture f(0, 5); - Storage storage(&f.part); - char nvs_key[16] = ""; - - TEST_ESP_OK(storage.init(0, 5)); - - /* Four pages should fit roughly 12 blobs*/ - for(uint8_t count = 1; count <= 12; count++) { - sprintf(nvs_key, "key:%u", count); - TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob))); - } - - for(uint8_t count = 13; count <= 20; count++) { - sprintf(nvs_key, "key:%u", count); - TEST_ESP_ERR(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE); - } -} - -TEST_CASE("Check for nvs version incompatibility", "[nvs]") -{ - PartitionEmulationFixture f(0, 3); - - int32_t val1 = 0x12345678; - Page p; - p.load(&f.part, 0); - TEST_ESP_OK(p.setVersion(Page::NVS_VERSION - 1)); - TEST_ESP_OK(p.writeItem(1, ItemType::I32, "foo", &val1, sizeof(val1))); - - TEST_ESP_ERR(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3), - ESP_ERR_NVS_NEW_VERSION_FOUND); - - // if something went wrong, clean up - nvs_flash_deinit_partition(f.part.get_partition_name()); -} - TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") { SpiFlashEmulator emu("../nvs_partition_generator/part_old_blob_format.bin"); PartitionEmulation part(&emu, 0, 2 * SPI_FLASH_SEC_SIZE, "test"); nvs_handle_t handle; - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&part, 0, 2) ); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(&part, 0, 2) ); TEST_ESP_OK( nvs_open_from_partition("test", "dummyNamespace", NVS_READWRITE, &handle)); char buf[64] = {0}; @@ -2400,24 +800,24 @@ TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen)); CHECK(memcmp(buf, base64data, buflen) == 0); - Page p; + nvs::Page p; p.load(&part, 0); /* Check that item is stored in old format without blob index*/ - TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "dummyHex2BinKey")); + TEST_ESP_OK(p.findItem(1, nvs::ItemType::BLOB, "dummyHex2BinKey")); /* Modify the blob so that it is stored in the new format*/ hexdata[0] = hexdata[1] = hexdata[2] = 0x99; TEST_ESP_OK(nvs_set_blob(handle, "dummyHex2BinKey", hexdata, sizeof(hexdata))); - Page p2; + nvs::Page p2; p2.load(&part, 0); /* Check the type of the blob. Expect type mismatch since the blob is stored in new format*/ - TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "dummyHex2BinKey"), ESP_ERR_NVS_TYPE_MISMATCH); + TEST_ESP_ERR(p2.findItem(1, nvs::ItemType::BLOB, "dummyHex2BinKey"), ESP_ERR_NVS_TYPE_MISMATCH); /* Check that index is present for the modified blob according to new format*/ - TEST_ESP_OK(p2.findItem(1, ItemType::BLOB_IDX, "dummyHex2BinKey")); + TEST_ESP_OK(p2.findItem(1, nvs::ItemType::BLOB_IDX, "dummyHex2BinKey")); /* Read the blob in new format and check the contents*/ buflen = 64; @@ -2427,224 +827,12 @@ TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") TEST_ESP_OK(nvs_flash_deinit_partition(part.get_partition_name())); } -// TODO: leaks memory -TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") -{ - std::random_device rd; - std::mt19937 gen(rd()); - uint32_t seed = 3; - gen.seed(seed); - - PartitionEmulationFixture f(0, 10); - f.emu.randomize(seed); - f.emu.clearStats(); - - const uint32_t NVS_FLASH_SECTOR = 2; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; - static const size_t smallBlobLen = Page::CHUNK_MAX_SIZE / 3; - - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - - nvs_handle_t handle; - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); - RandomTest test; - - for ( uint8_t it = 0; it < 10; it++) { - size_t count = 200; - - /* Erase index and chunks for the blob with "singlepage" key */ - for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { - Page p; - p.load(&f.part, num); - p.eraseItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY); - p.eraseItem(1, ItemType::BLOB_IDX, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY); - p.eraseItem(1, ItemType::BLOB_DATA, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY); - } - - /* Now write "singlepage" blob in old format*/ - for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { - Page p; - p.load(&f.part, num); - if (p.state() == Page::PageState::ACTIVE) { - uint8_t buf[smallBlobLen]; - size_t blobLen = gen() % smallBlobLen; - - if(blobLen > p.getVarDataTailroom()) { - blobLen = p.getVarDataTailroom(); - } - - std::generate_n(buf, blobLen, [&]() -> uint8_t { - return static_cast(gen() % 256); - }); - - TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", buf, blobLen, Item::CHUNK_ANY)); - TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage")); - test.handleExternalWriteAtIndex(9, buf, blobLen); // This assumes "singlepage" is always at index 9 - - break; - } - } - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); - /* Initialize again */ - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, - NVS_FLASH_SECTOR, - NVS_FLASH_SECTOR_COUNT_MIN)); - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); - - /* Perform random things */ - auto res = test.doRandomThings(handle, gen, count); - if (res != ESP_OK) { - nvs_dump(NVS_DEFAULT_PART_NAME); - CHECK(0); - } - - /* Check that only one version is present for "singlepage". Its possible that last iteration did not write - * anything for "singlepage". So either old version or new version should be present.*/ - bool oldVerPresent = false, newVerPresent = false; - - for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { - Page p; - p.load(&f.part, num); - if(!oldVerPresent && p.findItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY) == ESP_OK) { - oldVerPresent = true; - } - - if(!newVerPresent && p.findItem(1, ItemType::BLOB_IDX, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY) == ESP_OK) { - newVerPresent = true; - } - } - CHECK(oldVerPresent != newVerPresent); - } - - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); - s_perf << "Monkey test: nErase=" << f.emu.getEraseOps() << " nWrite=" << f.emu.getWriteOps() << std::endl; -} - -TEST_CASE("Recovery from power-off during modification of blob present in old-format (same page)", "[nvs]") -{ - std::random_device rd; - std::mt19937 gen(rd()); - uint32_t seed = 3; - gen.seed(seed); - - PartitionEmulationFixture f(0, 3); - f.emu.clearStats(); - - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); - - nvs_handle_t handle; - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); - - uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef}; - uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee}; - size_t buflen = sizeof(hexdata); - uint8_t buf[Page::CHUNK_MAX_SIZE]; - - /* Power-off when blob was being written on the same page where its old version in old format - * was present*/ - Page p; - p.load(&f.part, 0); - /* Write blob in old-format*/ - TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old))); - - /* Write blob in new format*/ - TEST_ESP_OK(p.writeItem(1, ItemType::BLOB_DATA, "singlepage", hexdata, sizeof(hexdata), 0)); - /* All pages are stored. Now store the index.*/ - Item item; - item.blobIndex.dataSize = sizeof(hexdata); - item.blobIndex.chunkCount = 1; - item.blobIndex.chunkStart = VerOffset::VER_0_OFFSET; - - TEST_ESP_OK(p.writeItem(1, ItemType::BLOB_IDX, "singlepage", item.data, sizeof(item.data))); - - TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage")); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); - /* Initialize again */ - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); - - TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen)); - CHECK(memcmp(buf, hexdata, buflen) == 0); - - Page p2; - p2.load(&f.part, 0); - TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_TYPE_MISMATCH); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - -TEST_CASE("Recovery from power-off during modification of blob present in old-format (different page)", "[nvs]") -{ - std::random_device rd; - std::mt19937 gen(rd()); - uint32_t seed = 3; - gen.seed(seed); - - PartitionEmulationFixture f(0, 3); - f.emu.clearStats(); - - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); - - nvs_handle_t handle; - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); - - uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef}; - uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee}; - size_t buflen = sizeof(hexdata); - uint8_t buf[Page::CHUNK_MAX_SIZE]; - - - /* Power-off when blob was being written on the different page where its old version in old format - * was present*/ - Page p; - p.load(&f.part, 0); - /* Write blob in old-format*/ - TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old))); - - /* Write blob in new format*/ - TEST_ESP_OK(p.writeItem(1, ItemType::BLOB_DATA, "singlepage", hexdata, sizeof(hexdata), 0)); - /* All pages are stored. Now store the index.*/ - Item item; - item.blobIndex.dataSize = sizeof(hexdata); - item.blobIndex.chunkCount = 1; - item.blobIndex.chunkStart = VerOffset::VER_0_OFFSET; - p.markFull(); - Page p2; - p2.load(&f.part, 1); - p2.setSeqNumber(1); - - TEST_ESP_OK(p2.writeItem(1, ItemType::BLOB_IDX, "singlepage", item.data, sizeof(item.data))); - - TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage")); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); - /* Initialize again */ - TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); - - TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen)); - CHECK(memcmp(buf, hexdata, buflen) == 0); - - Page p3; - p3.load(&f.part, 0); - TEST_ESP_ERR(p3.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_NOT_FOUND); - - TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); -} - static void check_nvs_part_gen_args(SpiFlashEmulator *spi_flash_emulator, - char const *part_name, - int size, - char const *filename, - bool is_encr, - nvs_sec_cfg_t* xts_cfg) + char const *part_name, + int size, + char const *filename, + bool is_encr, + nvs_sec_cfg_t *xts_cfg) { nvs_handle_t handle; @@ -2653,17 +841,17 @@ static void check_nvs_part_gen_args(SpiFlashEmulator *spi_flash_emulator, esp_part.address = 0; esp_part.size = size * SPI_FLASH_SEC_SIZE; strncpy(esp_part.label, part_name, PART_NAME_MAX_SIZE); - shared_ptr part; + shared_ptr part; if (is_encr) { - NVSEncryptedPartition *enc_part = new NVSEncryptedPartition(&esp_part); + nvs::NVSEncryptedPartition *enc_part = new nvs::NVSEncryptedPartition(&esp_part); TEST_ESP_OK(enc_part->init(xts_cfg)); part.reset(enc_part); } else { part.reset(new PartitionEmulation(spi_flash_emulator, 0, size, part_name)); } - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(part.get(), 0, size) ); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(part.get(), 0, size) ); TEST_ESP_OK( nvs_open_from_partition(part_name, "dummyNamespace", NVS_READONLY, &handle)); uint8_t u8v; @@ -2730,11 +918,11 @@ static void check_nvs_part_gen_args(SpiFlashEmulator *spi_flash_emulator, } static void check_nvs_part_gen_args_mfg(SpiFlashEmulator *spi_flash_emulator, - char const *part_name, - int size, - char const *filename, - bool is_encr, - nvs_sec_cfg_t* xts_cfg) + char const *part_name, + int size, + char const *filename, + bool is_encr, + nvs_sec_cfg_t *xts_cfg) { nvs_handle_t handle; @@ -2743,17 +931,17 @@ static void check_nvs_part_gen_args_mfg(SpiFlashEmulator *spi_flash_emulator, esp_part.address = 0; esp_part.size = size * SPI_FLASH_SEC_SIZE; strncpy(esp_part.label, part_name, PART_NAME_MAX_SIZE); - shared_ptr part; + shared_ptr part; if (is_encr) { - NVSEncryptedPartition *enc_part = new NVSEncryptedPartition(&esp_part); + nvs::NVSEncryptedPartition *enc_part = new nvs::NVSEncryptedPartition(&esp_part); TEST_ESP_OK(enc_part->init(xts_cfg)); part.reset(enc_part); } else { part.reset(new PartitionEmulation(spi_flash_emulator, 0, size, part_name)); } - TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(part.get(), 0, size) ); + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()->init_custom(part.get(), 0, size) ); TEST_ESP_OK( nvs_open_from_partition(part_name, "dummyNamespace", NVS_READONLY, &handle)); uint8_t u8v; @@ -2821,7 +1009,7 @@ TEST_CASE("check and read data from partition generated via partition generation exit(execlp("cp", " cp", "-rf", "../nvs_partition_generator/testdata", - ".",NULL)); + ".", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -2831,15 +1019,15 @@ TEST_CASE("check and read data from partition generated via partition generation if (childpid == 0) { exit(execlp("python", "python", - "../nvs_partition_generator/nvs_partition_gen.py", - "generate", - "../nvs_partition_generator/sample_singlepage_blob.csv", - "partition_single_page.bin", - "0x3000", - "--version", - "1", - "--outdir", - "../nvs_partition_generator",NULL)); + "../nvs_partition_generator/nvs_partition_gen.py", + "generate", + "../nvs_partition_generator/sample_singlepage_blob.csv", + "partition_single_page.bin", + "0x3000", + "--version", + "1", + "--outdir", + "../nvs_partition_generator", NULL)); } else { CHECK(childpid > 0); int status; @@ -2856,7 +1044,7 @@ TEST_CASE("check and read data from partition generated via partition generation if (childpid == 0) { exit(execlp("rm", " rm", "-rf", - "testdata",NULL)); + "testdata", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -2873,7 +1061,7 @@ TEST_CASE("check and read data from partition generated via partition generation exit(execlp("cp", " cp", "-rf", "../nvs_partition_generator/testdata", - ".",NULL)); + ".", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -2883,15 +1071,15 @@ TEST_CASE("check and read data from partition generated via partition generation if (childpid == 0) { exit(execlp("python", "python", - "../nvs_partition_generator/nvs_partition_gen.py", - "generate", - "../nvs_partition_generator/sample_multipage_blob.csv", - "partition_multipage_blob.bin", - "0x4000", - "--version", - "2", - "--outdir", - "../nvs_partition_generator",NULL)); + "../nvs_partition_generator/nvs_partition_gen.py", + "generate", + "../nvs_partition_generator/sample_multipage_blob.csv", + "partition_multipage_blob.bin", + "0x4000", + "--version", + "2", + "--outdir", + "../nvs_partition_generator", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -2901,13 +1089,13 @@ TEST_CASE("check and read data from partition generated via partition generation SpiFlashEmulator emu("../nvs_partition_generator/partition_multipage_blob.bin"); - check_nvs_part_gen_args(&emu, "test", 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin",false,NULL); + check_nvs_part_gen_args(&emu, "test", 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", false, NULL); childpid = fork(); if (childpid == 0) { exit(execlp("rm", " rm", "-rf", - "testdata",NULL)); + "testdata", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -2945,7 +1133,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "--outdir", "../../../tools/mass_mfg/host_test", "--version", - "1",NULL)); + "1", NULL)); } else { CHECK(childpid > 0); @@ -2961,7 +1149,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "../nvs_partition_generator/Test-1-partition.bin", "0x3000", "--version", - "1",NULL)); + "1", NULL)); } else { CHECK(childpid > 0); @@ -2987,7 +1175,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "-c", "rm -rf ../../../tools/mass_mfg/host_test | \ rm -rf mfg_testdata | \ - rm -rf testdata",NULL)); + rm -rf testdata", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3026,7 +1214,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "--outdir", "../../../tools/mass_mfg/host_test", "--version", - "1",NULL)); + "1", NULL)); } else { CHECK(childpid > 0); @@ -3042,7 +1230,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "../nvs_partition_generator/Test-1-partition.bin", "0x3000", "--version", - "1",NULL)); + "1", NULL)); } else { CHECK(childpid > 0); @@ -3068,7 +1256,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "-c", "rm -rf ../../../tools/mass_mfg/host_test | \ rm -rf mfg_testdata | \ - rm -rf testdata",NULL)); + rm -rf testdata", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3089,7 +1277,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "rm -rf ../../../tools/mass_mfg/host_test | \ cp -rf ../../../tools/mass_mfg/testdata mfg_testdata | \ cp -rf ../nvs_partition_generator/testdata . | \ - mkdir -p ../../../tools/mass_mfg/host_test",NULL)); + mkdir -p ../../../tools/mass_mfg/host_test", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3107,7 +1295,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "--outdir", "../../../tools/mass_mfg/host_test", "--version", - "2",NULL)); + "2", NULL)); } else { CHECK(childpid > 0); @@ -3123,7 +1311,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "../nvs_partition_generator/Test-1-partition.bin", "0x4000", "--version", - "2",NULL)); + "2", NULL)); } else { CHECK(childpid > 0); @@ -3148,7 +1336,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "-c", "rm -rf ../../../tools/mass_mfg/host_test | \ rm -rf mfg_testdata | \ - rm -rf testdata",NULL)); + rm -rf testdata", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3186,7 +1374,7 @@ TEST_CASE("check underlying xts code for 32-byte size sector encryption", "[nvs] }; uint8_t eky_hex[2 * NVS_KEY_SIZE]; - uint8_t ptxt_hex[Page::ENTRY_SIZE], ctxt_hex[Page::ENTRY_SIZE], ba_hex[16]; + uint8_t ptxt_hex[nvs::Page::ENTRY_SIZE], ctxt_hex[nvs::Page::ENTRY_SIZE], ba_hex[16]; mbedtls_aes_xts_context ectx[1]; mbedtls_aes_xts_context dctx[1]; @@ -3203,11 +1391,11 @@ TEST_CASE("check underlying xts code for 32-byte size sector encryption", "[nvs] "33333333330000000000000000000000" }; - char ptxt[][2 * Page::ENTRY_SIZE + 1] = { + char ptxt[][2 * nvs::Page::ENTRY_SIZE + 1] = { "0000000000000000000000000000000000000000000000000000000000000000", "4444444444444444444444444444444444444444444444444444444444444444" }; - char ctxt[][2 * Page::ENTRY_SIZE + 1] = { + char ctxt[][2 * nvs::Page::ENTRY_SIZE + 1] = { "d456b4fc2e620bba6ffbed27b956c9543454dd49ebd8d8ee6f94b65cbe158f73", "e622334f184bbce129a25b2ac76b3d92abf98e22df5bdd15af471f3db8946a85" }; @@ -3225,9 +1413,9 @@ TEST_CASE("check underlying xts code for 32-byte size sector encryption", "[nvs] CHECK(!mbedtls_aes_xts_setkey_enc(ectx, eky_hex, 2 * NVS_KEY_SIZE * 8)); CHECK(!mbedtls_aes_xts_setkey_enc(dctx, eky_hex, 2 * NVS_KEY_SIZE * 8)); - CHECK(!mbedtls_aes_crypt_xts(ectx, MBEDTLS_AES_ENCRYPT, Page::ENTRY_SIZE, ba_hex, ptxt_hex, ptxt_hex)); + CHECK(!mbedtls_aes_crypt_xts(ectx, MBEDTLS_AES_ENCRYPT, nvs::Page::ENTRY_SIZE, ba_hex, ptxt_hex, ptxt_hex)); - CHECK(!memcmp(ptxt_hex, ctxt_hex, Page::ENTRY_SIZE)); + CHECK(!memcmp(ptxt_hex, ctxt_hex, nvs::Page::ENTRY_SIZE)); } } @@ -3249,7 +1437,7 @@ TEST_CASE("test nvs apis with encryption enabled", "[nvs]") for (uint16_t i = NVS_FLASH_SECTOR; i + TEST_ESP_OK( nvs::NVSPartitionManager::get_instance()-> init_custom(&fixture.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); @@ -3293,7 +1481,6 @@ TEST_CASE("test nvs apis with encryption enabled", "[nvs]") nvs_close(handle_1); nvs_close(handle_2); TEST_ESP_OK(nvs_flash_deinit()); - } TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled", "[nvs_part_gen]") @@ -3304,7 +1491,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena exit(execlp("cp", " cp", "-rf", "../nvs_partition_generator/testdata", - ".",NULL)); + ".", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3314,15 +1501,15 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena if (childpid == 0) { exit(execlp("python", "python", - "../nvs_partition_generator/nvs_partition_gen.py", - "encrypt", - "../nvs_partition_generator/sample_multipage_blob.csv", - "partition_encrypted.bin", - "0x4000", - "--inputkey", - "../nvs_partition_generator/testdata/sample_encryption_keys.bin", - "--outdir", - "../nvs_partition_generator",NULL)); + "../nvs_partition_generator/nvs_partition_gen.py", + "encrypt", + "../nvs_partition_generator/sample_multipage_blob.csv", + "partition_encrypted.bin", + "0x4000", + "--inputkey", + "../nvs_partition_generator/testdata/sample_encryption_keys.bin", + "--outdir", + "../nvs_partition_generator", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3333,7 +1520,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted.bin"); nvs_sec_cfg_t cfg; - for(int count = 0; count < NVS_KEY_SIZE; count++) { + for (int count = 0; count < NVS_KEY_SIZE; count++) { cfg.eky[count] = 0x11; cfg.tky[count] = 0x22; } @@ -3344,7 +1531,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena if (childpid == 0) { exit(execlp("rm", " rm", "-rf", - "testdata",NULL)); + "testdata", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3393,7 +1580,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena exit(execlp("cp", " cp", "-rf", "../nvs_partition_generator/testdata", - ".",NULL)); + ".", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3404,7 +1591,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena if (childpid == 0) { exit(execlp("rm", " rm", "-rf", - "../nvs_partition_generator/keys",NULL)); + "../nvs_partition_generator/keys", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3420,7 +1607,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena "0x4000", "--keygen", "--outdir", - "../nvs_partition_generator",NULL)); + "../nvs_partition_generator", NULL)); } else { CHECK(childpid > 0); @@ -3439,15 +1626,12 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena char *file_ext; dir = opendir("../nvs_partition_generator/keys"); - while ((file = readdir(dir)) != NULL) - { + while ((file = readdir(dir)) != NULL) { filename = file->d_name; files = strrchr(filename, '.'); - if (files != NULL) - { - file_ext = files+1; - if (strncmp(file_ext,"bin",3) == 0) - { + if (files != NULL) { + file_ext = files + 1; + if (strncmp(file_ext, "bin", 3) == 0) { break; } } @@ -3459,16 +1643,16 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena char buffer[64]; FILE *fp; - fp = fopen(encr_file.c_str(),"rb"); - fread(buffer,sizeof(buffer),1,fp); + fp = fopen(encr_file.c_str(), "rb"); + fread(buffer, sizeof(buffer), 1, fp); fclose(fp); nvs_sec_cfg_t cfg; - for(int count = 0; count < NVS_KEY_SIZE; count++) { + for (int count = 0; count < NVS_KEY_SIZE; count++) { cfg.eky[count] = buffer[count] & 255; - cfg.tky[count] = buffer[count+32] & 255; + cfg.tky[count] = buffer[count + 32] & 255; } check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); @@ -3487,15 +1671,12 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena char *file_ext; dir = opendir("../nvs_partition_generator/keys"); - while ((file = readdir(dir)) != NULL) - { + while ((file = readdir(dir)) != NULL) { filename = file->d_name; files = strrchr(filename, '.'); - if (files != NULL) - { - file_ext = files+1; - if (strncmp(file_ext,"bin",3) == 0) - { + if (files != NULL) { + file_ext = files + 1; + if (strncmp(file_ext, "bin", 3) == 0) { break; } } @@ -3503,17 +1684,17 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena std::string encr_file = std::string("../nvs_partition_generator/keys/") + std::string(filename); - if (childpid == 0) { + if (childpid == 0) { exit(execlp("python", "python", - "../nvs_partition_generator/nvs_partition_gen.py", - "encrypt", - "../nvs_partition_generator/sample_multipage_blob.csv", - "partition_encrypted_using_keyfile.bin", - "0x4000", - "--inputkey", - encr_file.c_str(), - "--outdir", - "../nvs_partition_generator",NULL)); + "../nvs_partition_generator/nvs_partition_gen.py", + "encrypt", + "../nvs_partition_generator/sample_multipage_blob.csv", + "partition_encrypted_using_keyfile.bin", + "0x4000", + "--inputkey", + encr_file.c_str(), + "--outdir", + "../nvs_partition_generator", NULL)); } else { CHECK(childpid > 0); @@ -3526,16 +1707,16 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena char buffer[64]; FILE *fp; - fp = fopen(encr_file.c_str(),"rb"); - fread(buffer,sizeof(buffer),1,fp); + fp = fopen(encr_file.c_str(), "rb"); + fread(buffer, sizeof(buffer), 1, fp); fclose(fp); nvs_sec_cfg_t cfg; - for(int count = 0; count < NVS_KEY_SIZE; count++) { + for (int count = 0; count < NVS_KEY_SIZE; count++) { cfg.eky[count] = buffer[count] & 255; - cfg.tky[count] = buffer[count+32] & 255; + cfg.tky[count] = buffer[count + 32] & 255; } check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); @@ -3544,7 +1725,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena if (childpid == 0) { exit(execlp("rm", " rm", "-rf", - "../nvs_partition_generator/keys",NULL)); + "../nvs_partition_generator/keys", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3555,7 +1736,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena if (childpid == 0) { exit(execlp("rm", " rm", "-rf", - "testdata",NULL)); + "testdata", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3576,7 +1757,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "rm -rf ../../../tools/mass_mfg/host_test | \ cp -rf ../../../tools/mass_mfg/testdata mfg_testdata | \ cp -rf ../nvs_partition_generator/testdata . | \ - mkdir -p ../../../tools/mass_mfg/host_test",NULL)); + mkdir -p ../../../tools/mass_mfg/host_test", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3596,7 +1777,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "--version", "2", "--inputkey", - "mfg_testdata/sample_encryption_keys.bin",NULL)); + "mfg_testdata/sample_encryption_keys.bin", NULL)); } else { CHECK(childpid > 0); @@ -3614,7 +1795,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "--version", "2", "--inputkey", - "testdata/sample_encryption_keys.bin",NULL)); + "testdata/sample_encryption_keys.bin", NULL)); } else { CHECK(childpid > 0); @@ -3630,7 +1811,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin"); nvs_sec_cfg_t cfg; - for(int count = 0; count < NVS_KEY_SIZE; count++) { + for (int count = 0; count < NVS_KEY_SIZE; count++) { cfg.eky[count] = 0x11; cfg.tky[count] = 0x22; } @@ -3648,7 +1829,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "-c", "rm -rf ../../../tools/mass_mfg/host_test | \ rm -rf mfg_testdata | \ - rm -rf testdata",NULL)); + rm -rf testdata", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3669,7 +1850,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "rm -rf ../../../tools/mass_mfg/host_test | \ cp -rf ../../../tools/mass_mfg/testdata mfg_testdata | \ cp -rf ../nvs_partition_generator/testdata . | \ - mkdir -p ../../../tools/mass_mfg/host_test",NULL)); + mkdir -p ../../../tools/mass_mfg/host_test", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); @@ -3683,7 +1864,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "--outdir", "../../../tools/mass_mfg/host_test", "--keyfile", - "encr_keys_host_test.bin",NULL)); + "encr_keys_host_test.bin", NULL)); } else { CHECK(childpid > 0); @@ -3704,7 +1885,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "--version", "2", "--inputkey", - "../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin",NULL)); + "../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin", NULL)); } else { CHECK(childpid > 0); @@ -3722,7 +1903,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "--version", "2", "--inputkey", - "../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin",NULL)); + "../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin", NULL)); } else { CHECK(childpid > 0); @@ -3743,16 +1924,16 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit char buffer[64]; FILE *fp; - fp = fopen("../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin","rb"); - fread(buffer,sizeof(buffer),1,fp); + fp = fopen("../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin", "rb"); + fread(buffer, sizeof(buffer), 1, fp); fclose(fp); nvs_sec_cfg_t cfg; - for(int count = 0; count < NVS_KEY_SIZE; count++) { + for (int count = 0; count < NVS_KEY_SIZE; count++) { cfg.eky[count] = buffer[count] & 255; - cfg.tky[count] = buffer[count+32] & 255; + cfg.tky[count] = buffer[count + 32] & 255; } check_nvs_part_gen_args_mfg(&emu1, NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg); @@ -3768,7 +1949,7 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit "rm -rf keys | \ rm -rf mfg_testdata | \ rm -rf testdata | \ - rm -rf ../../../tools/mass_mfg/host_test",NULL)); + rm -rf ../../../tools/mass_mfg/host_test", NULL)); } else { CHECK(childpid > 0); waitpid(childpid, &status, 0); diff --git a/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp b/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp deleted file mode 100644 index f7f9e2a1d3..0000000000 --- a/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include "catch.hpp" -#include -#include -#include "nvs_test_api.h" -#include "nvs_handle_simple.hpp" -#include "nvs_partition_manager.hpp" -#include "spi_flash_emulation.h" - -#include "test_fixtures.hpp" - -#include -#include - -using namespace std; -using namespace nvs; - -TEST_CASE("open_nvs_handle too long namespace name", "[partition_mgr]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - const char *TOO_LONG_NS_NAME = "0123456789abcdef"; - char test_e = 'a'; - NVSHandleSimple *handle; - PartitionEmulationFixture f(0, 10, "test"); - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", TOO_LONG_NS_NAME, NVS_READWRITE, &handle) == ESP_ERR_NVS_KEY_TOO_LONG); - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); - -} - -TEST_CASE("open_nvs_handle longest namespace name", "[partition_mgr]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - const char *LONGEST_NS_NAME = "0123456789abcde"; - char test_e = 'a'; - NVSHandleSimple *handle; - PartitionEmulationFixture f(0, 10, "test"); - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", LONGEST_NS_NAME, NVS_READWRITE, &handle) == ESP_OK); - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1); - - delete handle; - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); - -} - -TEST_CASE("NVSHandleSimple closes its reference in PartitionManager", "[partition_mgr]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - PartitionEmulationFixture f(0, 10, "test"); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - - NVSHandleSimple *handle; - REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle) == ESP_OK); - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1); - - delete handle; - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); - -} - -TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[partition_mgr]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - PartitionEmulationFixture f(0, 10, "test"); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - - NVSHandleSimple *handle1; - NVSHandleSimple *handle2; - - REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle1) == ESP_OK); - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1); - - REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle2) == ESP_OK); - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 2); - - delete handle1; - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1); - - delete handle2; - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); - -} - -TEST_CASE("NVSHandleSimple readonly fails", "[partition_mgr]") -{ - PartitionEmulationFixture f(0, 10); - - NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME); - NVSHandleSimple *handle_1; - NVSHandleSimple *handle_2; - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - CHECK(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - - // first, creating namespace... - REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle_1) == ESP_OK); - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1); - - delete handle_1; - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READONLY, &handle_2) == ESP_OK); - CHECK(handle_2->set_item("key", 47) == ESP_ERR_NVS_READ_ONLY); - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1); - - delete handle_2; - - CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); - // without deinit it affects "nvs api tests" - CHECK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); -} - -TEST_CASE("NVSHandleSimple set/get char", "[partition_mgr]") -{ - enum class TestEnum : char { - FOO = -1, - BEER, - BAR - }; - - PartitionEmulationFixture f(0, 10); - - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - NVSHandleSimple *handle; - REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); - - char test_e = 'a'; - char test_e_read = 'z'; - - CHECK(handle->set_item("key", test_e) == ESP_OK); - - CHECK(handle->get_item("key", test_e_read) == ESP_OK); - CHECK(test_e == test_e_read); - - delete handle; - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); -} - -TEST_CASE("NVSHandleSimple correctly sets/gets int enum", "[partition_mgr]") -{ - enum class TestEnum : int { - FOO, - BAR - }; - - PartitionEmulationFixture f(0, 10); - - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - NVSHandleSimple *handle; - REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); - - TestEnum test_e = TestEnum::BAR; - TestEnum test_e_read = TestEnum::FOO; - - CHECK(handle->set_item("key", test_e) == ESP_OK); - - CHECK(handle->get_item("key", test_e_read) == ESP_OK); - CHECK(test_e == test_e_read); - - delete handle; - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); -} - -TEST_CASE("NVSHandleSimple correctly sets/gets int enum with negative values", "[partition_mgr]") -{ - enum class TestEnum : int { - FOO = -1, - BEER, - BAR - }; - - PartitionEmulationFixture f(0, 10); - - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - NVSHandleSimple *handle; - REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); - - TestEnum test_e = TestEnum::FOO; - TestEnum test_e_read = TestEnum::BEER; - - CHECK(handle->set_item("key", test_e) == ESP_OK); - - CHECK(handle->get_item("key", test_e_read) == ESP_OK); - CHECK(test_e == test_e_read); - - delete handle; - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); -} - -TEST_CASE("NVSHandleSimple correctly sets/gets uint8_t enum", "[partition_mgr]") -{ - enum class TestEnum : uint8_t { - FOO, - BAR - }; - - PartitionEmulationFixture f(0, 10); - - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - NVSHandleSimple *handle; - REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); - - TestEnum test_e = TestEnum::BAR; - TestEnum test_e_read = TestEnum::FOO; - - CHECK(handle->set_item("key", test_e) == ESP_OK); - - CHECK(handle->get_item("key", test_e_read) == ESP_OK); - CHECK(test_e == test_e_read); - - delete handle; - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); -} - -TEST_CASE("NVSHandleSimple correctly sets/gets char enum", "[partition_mgr]") -{ - enum class TestEnum : char { - FOO = -1, - BEER, - BAR - }; - - PartitionEmulationFixture f(0, 10); - - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - NVSHandleSimple *handle; - REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK); - - TestEnum test_e = TestEnum::BAR; - TestEnum test_e_read = TestEnum::FOO; - - CHECK(handle->set_item("key", test_e) == ESP_OK); - - CHECK(handle->get_item("key", test_e_read) == ESP_OK); - CHECK(test_e == test_e_read); - - delete handle; - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); -} diff --git a/components/nvs_flash/test_nvs_host/test_nvs_initialization.cpp b/components/nvs_flash/test_nvs_host/test_nvs_initialization.cpp deleted file mode 100644 index 7731d1da51..0000000000 --- a/components/nvs_flash/test_nvs_host/test_nvs_initialization.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// 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. -#include "catch.hpp" -#include "nvs_partition_manager.hpp" -#include "spi_flash_emulation.h" -#include "esp_partition.h" -#include "nvs.h" -#include - -using namespace nvs; - -TEST_CASE("nvs_flash_init_partition_ptr fails due to nullptr arg", "[nvs_custom_part]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); - - CHECK(nvs_flash_init_partition_ptr(nullptr) == ESP_ERR_INVALID_ARG); -} - -TEST_CASE("nvs_flash_init_partition_ptr inits one partition", "[nvs_custom_part]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); - - esp_partition_t partition = {}; - strcpy(partition.label, "test"); - partition.address = NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE; - partition.size = NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE; - - CHECK(nvs_flash_init_partition_ptr(&partition) == ESP_OK); - CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); - CHECK(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); -} diff --git a/components/nvs_flash/test_nvs_host/test_nvs_partition.cpp b/components/nvs_flash/test_nvs_host/test_nvs_partition.cpp index 4385b9a6dc..d394f525aa 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs_partition.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs_partition.cpp @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// 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. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "catch.hpp" #include #include @@ -18,38 +10,33 @@ #include "nvs_handle_simple.hpp" #include "nvs_partition.hpp" #include "spi_flash_emulation.h" - #include "test_fixtures.hpp" - #include -using namespace std; -using namespace nvs; - TEST_CASE("encrypted partition read size must be item size", "[nvs]") { char foo [32] = { }; nvs_sec_cfg_t xts_cfg; - for(int count = 0; count < NVS_KEY_SIZE; count++) { + for (int count = 0; count < NVS_KEY_SIZE; count++) { xts_cfg.eky[count] = 0x11; xts_cfg.tky[count] = 0x22; } EncryptedPartitionFixture fix(&xts_cfg); - CHECK(fix.part.read(0, foo, sizeof (foo) -1) == ESP_ERR_INVALID_SIZE); + CHECK(fix.part.read(0, foo, sizeof (foo) - 1) == ESP_ERR_INVALID_SIZE); } TEST_CASE("encrypted partition write size must be mod item size", "[nvs]") { char foo [64] = { }; nvs_sec_cfg_t xts_cfg; - for(int count = 0; count < NVS_KEY_SIZE; count++) { + for (int count = 0; count < NVS_KEY_SIZE; count++) { xts_cfg.eky[count] = 0x11; xts_cfg.tky[count] = 0x22; } EncryptedPartitionFixture fix(&xts_cfg); - CHECK(fix.part.write(0, foo, sizeof (foo) -1) == ESP_ERR_INVALID_SIZE); - CHECK(fix.part.write(0, foo, sizeof (foo)) == ESP_OK); - CHECK(fix.part.write(0, foo, sizeof (foo) * 2) == ESP_OK); + CHECK(fix.part.write(0, foo, sizeof (foo) - 1) == ESP_ERR_INVALID_SIZE); + CHECK(fix.part.write(0, foo, sizeof (foo) / 2) == ESP_OK); + CHECK(fix.part.write(sizeof(foo) / 2, foo, sizeof (foo)) == ESP_OK); } diff --git a/components/nvs_flash/test_nvs_host/test_nvs_storage.cpp b/components/nvs_flash/test_nvs_host/test_nvs_storage.cpp deleted file mode 100644 index a0ace089ce..0000000000 --- a/components/nvs_flash/test_nvs_host/test_nvs_storage.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// 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. -#include "catch.hpp" -#include -#include "nvs_test_api.h" -#include "nvs_storage.hpp" -#include "nvs_partition_manager.hpp" -#include "spi_flash_emulation.h" - -#include "test_fixtures.hpp" - -#include - -using namespace std; -using namespace nvs; - -TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs_storage]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - PartitionEmulationFixture f(0, 10, "test"); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - uint8_t blob [] = {0x0, 0x1, 0x2, 0x3}; - uint8_t blob_new [] = {0x3, 0x2, 0x1, 0x0}; - Storage *storage = NVSPartitionManager::get_instance()->lookup_storage_from_name("test"); - uint8_t ns_index; - storage->createOrOpenNamespace("test_ns", true, ns_index); - - CHECK(storage->writeItem(ns_index, ItemType::BLOB, "test_blob", blob, sizeof(blob)) == ESP_OK); - - // changing provokes a blob with version offset 1 (VerOffset::VER_1_OFFSET) - CHECK(storage->writeItem(ns_index, ItemType::BLOB, "test_blob", blob_new, sizeof(blob_new)) == ESP_OK); - - nvs_opaque_iterator_t it; - it.storage = storage; - it.type = NVS_TYPE_ANY; - - // Central check: does the iterator recognize the blob with version 1? - REQUIRE(storage->findEntry(&it, "test_ns")); - - CHECK(string(it.entry_info.namespace_name) == string("test_ns")); - CHECK(string(it.entry_info.key) == string("test_blob")); - CHECK(it.entry_info.type == NVS_TYPE_BLOB); - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); -} diff --git a/components/nvs_flash/test_nvs_host/test_partition_manager.cpp b/components/nvs_flash/test_nvs_host/test_partition_manager.cpp index a8d7b630a6..180fd6df89 100644 --- a/components/nvs_flash/test_nvs_host/test_partition_manager.cpp +++ b/components/nvs_flash/test_nvs_host/test_partition_manager.cpp @@ -11,34 +11,8 @@ #include "nvs_partition_manager.hpp" #include "spi_flash_emulation.h" #include "nvs_test_api.h" - #include "test_fixtures.hpp" -using namespace nvs; - -TEST_CASE("Partition manager initializes storage", "[partition_mgr]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - PartitionEmulationFixture f(0, 10, "test"); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); - CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(f.part.get_partition_name()) == ESP_OK); -} - -TEST_CASE("Partition manager de-initializes storage", "[partition_mgr]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - PartitionEmulationFixture f(0, 10, "test"); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); - CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); - CHECK(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); - CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") == nullptr); -} - TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]") { const uint32_t NVS_FLASH_SECTOR = 6; @@ -47,52 +21,17 @@ TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]" PartitionEmulation part_0(&emu, NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE, NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE, "test1"); PartitionEmulation part_1(&emu, NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE, NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE, "test2"); - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&part_0, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&part_0, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); // TODO: why does this work, actually? same sectors used as above - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&part_1, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&part_1, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); - Storage *storage1 = NVSPartitionManager::get_instance()->lookup_storage_from_name("test1"); + nvs::Storage *storage1 = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test1"); REQUIRE(storage1 != nullptr); - Storage *storage2 = NVSPartitionManager::get_instance()->lookup_storage_from_name("test2"); + nvs::Storage *storage2 = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test2"); REQUIRE(storage2 != nullptr); CHECK(storage1 != storage2); - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(part_0.get_partition_name()) == ESP_OK); - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(part_1.get_partition_name()) == ESP_OK); -} - -TEST_CASE("Partition manager open fails on null handle", "[partition_mgr]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - PartitionEmulationFixture f(0, 10, "test"); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - CHECK(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, nullptr) - == ESP_ERR_INVALID_ARG); - - NVSPartitionManager::get_instance()->deinit_partition("test"); -} - -TEST_CASE("Partition manager invalidates handle on partition de-init", "[partition_mgr]") -{ - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - PartitionEmulationFixture f(0, 10, "test"); - - REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_OK); - - NVSHandleSimple *handle; - REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle) == ESP_OK); - CHECK(handle->erase_all() == ESP_OK); - - REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); - - CHECK(handle->erase_all() == ESP_ERR_NVS_INVALID_HANDLE); - - delete handle; + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(part_0.get_partition_name()) == ESP_OK); + REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(part_1.get_partition_name()) == ESP_OK); } diff --git a/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp b/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp index aa9c05f1c6..5a371a5b66 100644 --- a/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp +++ b/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp @@ -9,12 +9,10 @@ #include "spi_flash_emulation.h" #include -using namespace std; - template bool range_empty_n(Tit it_begin, size_t n) { - return all_of(it_begin, it_begin + n, bind(equal_to(), placeholders::_1, 0xffffffff)); + return std::all_of(it_begin, it_begin + n, bind(std::equal_to(), std::placeholders::_1, 0xffffffff)); } struct FlashEmuFixture { diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 4417561e26..7ffee83210 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -800,7 +800,6 @@ components/mqtt/host_test/mocks/include/freertos/FreeRTOSConfig.h components/mqtt/host_test/mocks/include/freertos/portmacro.h components/mqtt/host_test/mocks/include/machine/endian.h components/mqtt/host_test/mocks/include/sys/queue.h -components/nvs_flash/host_test/fixtures/test_fixtures.hpp components/nvs_flash/host_test/nvs_page_test/main/nvs_page_test.cpp components/nvs_flash/include/nvs_flash.h components/nvs_flash/include/nvs_handle.hpp @@ -827,12 +826,10 @@ components/nvs_flash/test/test_nvs.c components/nvs_flash/test_nvs_host/esp_error_check_stub.cpp components/nvs_flash/test_nvs_host/main.cpp components/nvs_flash/test_nvs_host/sdkconfig.h -components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp components/nvs_flash/test_nvs_host/test_fixtures.hpp components/nvs_flash/test_nvs_host/test_intrusive_list.cpp components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp components/nvs_flash/test_nvs_host/test_nvs_initialization.cpp -components/nvs_flash/test_nvs_host/test_nvs_partition.cpp components/nvs_flash/test_nvs_host/test_nvs_storage.cpp components/openthread/include/esp_openthread_lock.h components/protocomm/include/transports/protocomm_console.h diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake index b0b8f405cb..d4ca3d70f0 100644 --- a/tools/cmake/project.cmake +++ b/tools/cmake/project.cmake @@ -516,11 +516,18 @@ macro(project project_name) __component_get_target(build_component_target ${build_component}) __component_get_property(whole_archive ${build_component_target} WHOLE_ARCHIVE) if(whole_archive) - message(STATUS "Component ${build_component} will be linked with -Wl,--whole-archive") - target_link_libraries(${project_elf} PRIVATE - "-Wl,--whole-archive" - ${build_component} - "-Wl,--no-whole-archive") + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + message(STATUS "Component ${build_component} will be linked with -Wl,-force_load") + target_link_libraries(${project_elf} PRIVATE + "-Wl,-force_load" + ${build_component}) + else() + message(STATUS "Component ${build_component} will be linked with -Wl,--whole-archive") + target_link_libraries(${project_elf} PRIVATE + "-Wl,--whole-archive" + ${build_component} + "-Wl,--no-whole-archive") + endif() else() target_link_libraries(${project_elf} PRIVATE ${build_component}) endif()