diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index 41fe7d8006..7ce2871c1a 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -1,3 +1,5 @@ +idf_build_get_property(target IDF_TARGET) + set(srcs "src/nvs_api.cpp" "src/nvs_cxx_api.cpp" "src/nvs_item_hash_list.cpp" @@ -11,10 +13,39 @@ set(srcs "src/nvs_api.cpp" "src/nvs_partition_manager.cpp" "src/nvs_types.cpp") -if(CONFIG_NVS_ENCRYPTION) - list(APPEND srcs "src/nvs_encrypted_partition.cpp") -endif() +set(public_req spi_flash) + +set(include_dirs "include") idf_component_register(SRCS "${srcs}" - REQUIRES spi_flash mbedtls - INCLUDE_DIRS include) + REQUIRES "${public_req}" + INCLUDE_DIRS "${include_dirs}") + +# If we use the linux target, we need to redirect the crc functions to the linux +if(${target} STREQUAL "linux") + if(CONFIG_NVS_ENCRYPTION) + # mbedtls isn't configured for building with linux or as mock target. It will draw in all kind of dependencies + message(FATAL_ERROR "NVS currently doesn't support encryption if built for Linux.") + endif() + idf_component_get_property(spi_flash_dir spi_flash COMPONENT_DIR) + target_include_directories(${COMPONENT_LIB} PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/mock/int" + "${spi_flash_dir}/sim/stubs/freertos/include") + target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/mock/int/crc.cpp") + target_compile_options(${COMPONENT_LIB} PUBLIC "-DLINUX_TARGET") +else() + # TODO: this is a workaround until IDF-2085 is fixed + idf_component_get_property(mbedtls_lib mbedtls COMPONENT_LIB) + target_link_libraries(${COMPONENT_LIB} PUBLIC ${mbedtls_lib}) +endif() + +if(CONFIG_NVS_ENCRYPTION) + target_sources(${COMPONENT_LIB} PRIVATE "src/nvs_encrypted_partition.cpp") + idf_component_get_property(mbedtls_lib mbedtls COMPONENT_LIB) + target_link_libraries(${COMPONENT_LIB} PUBLIC ${mbedtls_lib}) +endif() + +if(${target} STREQUAL "linux") + target_compile_options(${COMPONENT_LIB} PUBLIC --coverage) + target_link_libraries(${COMPONENT_LIB} PUBLIC --coverage) +endif() diff --git a/components/nvs_flash/host_test/fixtures/test_fixtures.hpp b/components/nvs_flash/host_test/fixtures/test_fixtures.hpp new file mode 100644 index 0000000000..06b12ce176 --- /dev/null +++ b/components/nvs_flash/host_test/fixtures/test_fixtures.hpp @@ -0,0 +1,429 @@ +// 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 "nvs_partition.hpp" +#include "nvs.h" +#include "nvs_page.hpp" +#include "nvs_storage.hpp" +#include +#include + +#ifdef CONFIG_NVS_ENCRYPTION +#include "nvs_encrypted_partition.hpp" +#endif + +extern "C" { +#include "Mockesp_partition.h" +} + +struct FixtureException : std::exception { + FixtureException(const std::string& msg) : msg(msg) { } + + const char *what() { + return msg.c_str(); + } + + std::string msg; +}; + +class PartitionMock : public nvs::Partition { +public: + PartitionMock(uint32_t address, uint32_t size) + : partition(), address(address), size(size) + { + assert(size); + } + + const char *get_partition_name() override + { + return ""; + } + + esp_err_t read_raw(size_t src_offset, void* dst, size_t size) override + { + return esp_partition_read_raw(&partition, src_offset, dst, size); + } + + esp_err_t read(size_t src_offset, void* dst, size_t size) override + { + return esp_partition_read(&partition, src_offset, dst, size); + } + + esp_err_t write_raw(size_t dst_offset, const void* src, size_t size) override + { + return esp_partition_write_raw(&partition, dst_offset, src, size); + } + + esp_err_t write(size_t dst_offset, const void* src, size_t size) override + { + return esp_partition_write(&partition, dst_offset, src, size); + } + + esp_err_t erase_range(size_t dst_offset, size_t size) override + { + return esp_partition_erase_range(&partition, dst_offset, size); + } + + uint32_t get_address() override + { + return address; + } + + uint32_t get_size() override + { + return size; + } + + const esp_partition_t partition; + +private: + uint32_t address; + + uint32_t size; +}; + +#ifdef CONFIG_NVS_ENCRYPTION +struct EncryptedPartitionFixture { + EncryptedPartitionFixture(nvs_sec_cfg_t *cfg, + uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : esp_partition(), emu(start_sector + sector_size), + part(partition_name, &esp_partition) { + esp_partition.address = start_sector * SPI_FLASH_SEC_SIZE; + esp_partition.size = sector_size * SPI_FLASH_SEC_SIZE; + assert(part.init(cfg) == ESP_OK); + } + + ~EncryptedPartitionFixture() { } + + esp_partition_t esp_partition; + + SpiFlashEmulator emu; + + nvs::NVSEncryptedPartition part; +}; +#endif + +struct PartitionMockFixture { + PartitionMockFixture(uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : part_mock(start_sector * SPI_FLASH_SEC_SIZE, sector_size * SPI_FLASH_SEC_SIZE) { + std::fill_n(raw_header, sizeof(raw_header)/sizeof(raw_header[0]), UINT8_MAX); + } + + ~PartitionMockFixture() { } + + uint8_t raw_header[512]; + + PartitionMock part_mock; +}; + +struct NVSPageFixture : public PartitionMockFixture { + NVSPageFixture(uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : PartitionMockFixture(start_sector, sector_size, partition_name), page() + { + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 32); + + for (int i = 0; i < 8; i++) { + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 512); + } + + if (page.load(&part_mock, start_sector) != ESP_OK) throw FixtureException("couldn't setup page"); + } + + nvs::Page page; +}; + +struct NVSValidPageFixture : public PartitionMockFixture { + const static uint8_t NS_INDEX = 1; + + // valid header + uint8_t raw_header_valid [32]; + + // entry table with one entry + uint8_t raw_entry_table [32]; + + uint8_t ns_entry [32]; + + uint8_t value_entry [32]; + + NVSValidPageFixture(uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : PartitionMockFixture(start_sector, sector_size, partition_name), + raw_header_valid {0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x16, 0xdd, 0xdc}, + ns_entry {0x00, 0x01, 0x01, 0xff, 0x68, 0xc5, 0x3f, 0x0b, 't', 'e', 's', 't', '_', 'n', 's', '\0', + '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + value_entry {0x01, 0x01, 0x01, 0xff, 0x3d, 0xf3, 0x99, 0xe5, 't', 'e', 's', 't', '_', 'v', 'a', 'l', + 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0', 47, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + page() + { + std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0); + raw_entry_table[0] = 0xfa; + + // read page header + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header_valid, 32); + + // read entry table + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_entry_table, 32); + + // read next free entry's header + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 4); + + // read namespace entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); + + // read normal entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(value_entry, 32); + + // read normal entry second time during duplicated entry check + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(value_entry, 32); + + if (page.load(&part_mock, start_sector) != ESP_OK) throw FixtureException("couldn't setup page"); + } + + nvs::Page page; +}; + +struct NVSValidStorageFixture : public PartitionMockFixture { + const static uint8_t NS_INDEX = 1; + + uint8_t ns_entry [32]; + + uint8_t empty_entry [32]; + + NVSValidStorageFixture(uint32_t start_sector = 0, + uint32_t sector_size = 3, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : PartitionMockFixture(start_sector, sector_size, partition_name), + ns_entry {0x00, 0x01, 0x01, 0xff, 0x68, 0xc5, 0x3f, 0x0b, 't', 'e', 's', 't', '_', 'n', 's', '\0', + '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + empty_entry(), + storage(&part_mock) + { + std::fill_n(empty_entry, sizeof(empty_entry)/sizeof(empty_entry[0]), 0xFF); + + // entry table with one entry + uint8_t raw_entry_table [32]; + + uint8_t header_full_page [] = { + 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x16, 0xdd, 0xdc}; + + uint8_t header_second_page [] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + uint8_t header_third_page [] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + // entry_table with all elements deleted except the namespace entry written and the last entry free + std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0); + raw_entry_table[0] = 0x02; + raw_entry_table[31] = 0xFC; + + // read full page header + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(header_full_page, 32); + + // read entry table + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_entry_table, 32); + + // reading entry table checks empty entry + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(empty_entry, 32); + + // read namespace entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); + + // read last two pages' headers, which trigger an automatic full read each because each page is empty + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(header_second_page, 32); + for (int i = 0; i < 8; i++) { + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 512); + } + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(header_third_page, 32); + for (int i = 0; i < 8; i++) { + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 512); + } + + // read namespace entry in duplicated header item check of pagemanager::load + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); + + // storage finally actually reads namespace + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); + + // storage looks for blob index entries + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); + + // Storage::eraseOrphanDataBlobs() also wants to take it's turn... + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); + + if (storage.init(start_sector, sector_size) != ESP_OK) throw FixtureException("couldn't setup page"); + } + + nvs::Storage storage; +}; + +struct NVSValidBlobPageFixture : public PartitionMockFixture { + const static uint8_t NS_INDEX = 1; + const static size_t BLOB_DATA_SIZE = 32; + + // valid header + uint8_t raw_header_valid [32]; + + // entry table with one entry + uint8_t raw_entry_table [32]; + + uint8_t ns_entry [32]; + + uint8_t blob_entry [32]; + uint8_t blob_data [BLOB_DATA_SIZE]; + uint8_t blob_index [32]; + + NVSValidBlobPageFixture(uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : PartitionMockFixture(start_sector, sector_size, partition_name), + raw_header_valid {0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x16, 0xdd, 0xdc}, + ns_entry {0x00, 0x01, 0x01, 0xff, 0x68, 0xc5, 0x3f, 0x0b, 't', 'e', 's', 't', '_', 'n', 's', '\0', + '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + blob_entry {0x01, 0x42, 0x02, 0x00, 0xaa, 0xf3, 0x23, 0x87, 't', 'e', 's', 't', '_', 'b', 'l', 'o', + 'b', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 0x20, 0x00, 0xff, 0xff, 0xc6, 0x96, 0x86, 0xd9}, + blob_data {0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}, + blob_index {0x01, 0x48, 0x01, 0xff, 0x42, 0x6b, 0xdf, 0x66, 't', 'e', 's', 't', '_', 'b', 'l', 'o', + 'b', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff}, + page() + { + std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0xFF); + raw_entry_table[0] = 0xaa; + + // read page header + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header_valid, 32); + + // read entry table + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_entry_table, 32); + + // read next free entry's header + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 4); + + // read namespace entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); + + // read normal blob entry + index, not the data + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(blob_entry, 32); + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(blob_index, 32); + + // read normal entry second time during duplicated entry check + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(blob_entry, 32); + + if (page.load(&part_mock, start_sector) != ESP_OK) throw FixtureException("couldn't setup page"); + } + + nvs::Page page; +}; + +struct NVSFullPageFixture : public PartitionMockFixture { + const static uint8_t NS_INDEX = 1; + + // valid header + uint8_t raw_header_valid [32]; + + // entry table with one entry + uint8_t raw_entry_table [32]; + + uint8_t ns_entry [32]; + + uint8_t value_entry [32]; + + NVSFullPageFixture(uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME, + bool load = true) + : PartitionMockFixture(start_sector, sector_size, partition_name), + raw_header_valid {0xfc, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, 0x48, 0x9f, 0x38}, + ns_entry {0x00, 0x01, 0x01, 0xff, 0x68, 0xc5, 0x3f, 0x0b, 't', 'e', 's', 't', '_', 'n', 's', '\0', + '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + value_entry {0x01, 0x01, 0x01, 0xff, 0x3d, 0xf3, 0x99, 0xe5, 't', 'e', 's', 't', '_', 'v', 'a', 'l', + 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0', 47, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + page() + { + std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0); + raw_entry_table[0] = 0xfa; + + // entry_table with all elements deleted except the namespace entry written and the last entry free + std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0); + raw_entry_table[0] = 0x0a; + raw_entry_table[31] = 0xFC; + + // read page header + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header_valid, 32); + + // read entry table + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_entry_table, 32); + + // no next free entry check, only one entry written + + // read namespace entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); + + // read normal entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(value_entry, 32); + + // no duplicated entry check + + if (load) { + if (page.load(&part_mock, start_sector) != ESP_OK) throw FixtureException("couldn't setup page"); + } + } + + nvs::Page page; +}; diff --git a/components/nvs_flash/host_test/nvs_page_test/CMakeLists.txt b/components/nvs_flash/host_test/nvs_page_test/CMakeLists.txt new file mode 100644 index 0000000000..cafc7c6999 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_page_test/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) +idf_build_set_property(CONFIG_SPI_FLASH_MOCK 1) +idf_build_set_property(COMPILE_DEFINITIONS "-DNO_DEBUG_STORAGE" APPEND) +project(host_nvs_page_test) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/build/coverage.info" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build" + COMMAND lcov --capture --directory . --output-file coverage.info + COMMENT "Create coverage report" + ) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/build/coverage_report/" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/build/coverage.info" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build" + COMMAND genhtml coverage.info --output-directory coverage_report/ + COMMENT "Turn coverage report into html-based visualization" + ) + +add_custom_target(coverage + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build" + DEPENDS "coverage_report/" + ) diff --git a/components/nvs_flash/host_test/nvs_page_test/README.rst b/components/nvs_flash/host_test/nvs_page_test/README.rst new file mode 100644 index 0000000000..c005b3f48d --- /dev/null +++ b/components/nvs_flash/host_test/nvs_page_test/README.rst @@ -0,0 +1,23 @@ +NVS Page Test for Host +====================== + +Build +----- + +First, make sure that the target is set to linux. +Run ``idf.py --preview set-target linux`` to be sure. +Then do a normal IDF build: ``idf.py build``. + +Run +--- + +IDF monitor doesn't work yet for Linux. +You have to run the app manually: ``./build/host_nvs_page_test.elf``. + +Coverage +--- + +To generate the coverage, run: ``idf.py coverage``. +Afterwards, you can view the coverage by opening ``build/coverage_report/index.html`` with your browser. +Note that you need to run the application at least once before generating the coverage information. +If you run it multiple times, the coverage information adds up. diff --git a/components/nvs_flash/host_test/nvs_page_test/main/CMakeLists.txt b/components/nvs_flash/host_test/nvs_page_test/main/CMakeLists.txt new file mode 100644 index 0000000000..d2af1b6a6c --- /dev/null +++ b/components/nvs_flash/host_test/nvs_page_test/main/CMakeLists.txt @@ -0,0 +1,10 @@ +idf_component_register(SRCS "nvs_page_test.cpp" + INCLUDE_DIRS + "." + "${CMAKE_CURRENT_SOURCE_DIR}/../../fixtures" + "${CMAKE_CURRENT_SOURCE_DIR}/../../../test_nvs_host" + "${CMAKE_CURRENT_SOURCE_DIR}/../../../src" + REQUIRES cmock nvs_flash spi_flash) + +target_compile_options(${COMPONENT_LIB} PUBLIC --coverage) +target_link_libraries(${COMPONENT_LIB} --coverage) diff --git a/components/nvs_flash/host_test/nvs_page_test/main/nvs_page_test.cpp b/components/nvs_flash/host_test/nvs_page_test/main/nvs_page_test.cpp new file mode 100644 index 0000000000..d3899be783 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_page_test/main/nvs_page_test.cpp @@ -0,0 +1,934 @@ +/* Hello World Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "unity.h" +#include "test_fixtures.hpp" + +extern "C" { +#include "Mockesp_partition.h" +} + +using namespace std; +using namespace nvs; + +void test_Page_load_reading_header_fails() +{ + PartitionMock mock(0, 4096); + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_ERR_INVALID_ARG); + Page page; + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state()); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, page.load(&mock, 0)); +} + +void test_Page_load_reading_data_fails() +{ + uint8_t header[64]; + std::fill_n(header, sizeof(header)/sizeof(header[0]), UINT8_MAX); + PartitionMock mock(0, 4096); + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(header, 32); + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_FAIL); + Page page; + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state()); + TEST_ASSERT_EQUAL(ESP_FAIL, page.load(&mock, 0)); +} + +void test_Page_load__uninitialized_page_has_0xfe() +{ + PartitionMockFixture fix; + Page page; + + fix.raw_header[511] = 0xfe; + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(fix.raw_header, 32); + + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(fix.raw_header, 512); + + // Page::load() should return ESP_OK, but state has to be corrupt + TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part_mock, 0)); + + TEST_ASSERT_EQUAL(Page::PageState::CORRUPT, page.state()); +} + +void test_Page_load__initialized_corrupt_header() +{ + PartitionMockFixture fix; + Page page; + + uint8_t raw_header_corrupt [] = {0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x16, 0xdd, 0xdc}; + + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header_corrupt, 32); + + // Page::load() should return ESP_OK, but state has to be corrupt + TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part_mock, 0)); + + TEST_ASSERT_EQUAL(Page::PageState::CORRUPT, page.state()); +} + +void test_Page_load_success() +{ + PartitionMockFixture fix; + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(fix.raw_header, 32); + for (int i = 0; i < 8; i++) { + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(fix.raw_header, 512); + } + Page page; + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state()); + TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part_mock, 0)); + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, page.state()); +} + +void test_Page_load_full_page() +{ + NVSFullPageFixture fix(0, 1, NVS_DEFAULT_PART_NAME, false); + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state()); + TEST_ASSERT_EQUAL(ESP_OK, fix.page.load(&fix.part_mock, 0)); + TEST_ASSERT_EQUAL(Page::PageState::FULL, fix.page.state()); +} +void test_Page_load__seq_number_0() +{ + NVSValidPageFixture fix; + + uint32_t seq_num; + fix.page.getSeqNumber(seq_num); + TEST_ASSERT_EQUAL(0, seq_num); +} + +void test_Page_erase__write_fail() +{ + NVSValidPageFixture fix; + + esp_partition_erase_range_ExpectAndReturn(&fix.part_mock.partition, 0, 4096, ESP_FAIL); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.erase()); + TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state()); +} + +void test_Page_erase__success() +{ + NVSValidPageFixture fix; + + esp_partition_erase_range_ExpectAndReturn(&fix.part_mock.partition, 0, 4096, ESP_OK); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.erase()); + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, fix.page.state()); +} + +void test_Page_write__initialize_write_failure() +{ + PartitionMockFixture fix; + uint8_t write_data = 47; + + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(fix.raw_header, 32); + for (int i = 0; i < 8; i++) { + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(fix.raw_header, 512); + } + esp_partition_write_raw_ExpectAnyArgsAndReturn(ESP_FAIL); + + Page page; + + TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part_mock, 0)); + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, page.state()); + + TEST_ASSERT_EQUAL(ESP_FAIL, page.writeItem(1, nvs::ItemType::U8, "test", &write_data, sizeof(write_data))); + TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state()); +} + +void test_Page_write__write_data_fails() +{ + NVSPageFixture fix; + uint8_t write_data = 47; + esp_partition_write_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_write_ExpectAnyArgsAndReturn(ESP_FAIL); + + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, fix.page.state()); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.writeItem(1, nvs::ItemType::U8, "test", &write_data, sizeof(write_data))); + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state()); +} + +void test_page_write__write_correct_entry_state() +{ + NVSPageFixture fix; + uint8_t write_data = 47; + uint8_t raw_result [4]; + std::fill_n(raw_result, sizeof(raw_result)/sizeof(raw_result[0]), UINT8_MAX); + // mark first entry as written + raw_result[0] = 0xfe; + + // initialize page + esp_partition_write_raw_ExpectAnyArgsAndReturn(ESP_OK); + + // write entry + esp_partition_write_ExpectAnyArgsAndReturn(ESP_OK); + + // write entry state + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, 1, 32, raw_result, 4, 4, ESP_OK); + + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, fix.page.state()); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.writeItem(1, nvs::ItemType::U8, "test_key", &write_data, sizeof(write_data))); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_write__write_correct_data() +{ + NVSPageFixture fix; + uint8_t write_data = 47; + uint8_t raw_result [32] = {0x01, 0x01, 0x01, 0xff, 0x98, 0x6f, 0x21, 0xfd, 't', 'e', 's', 't', '_', 'k', 'e', 'y', + '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 47, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + // initialize page + esp_partition_write_raw_ExpectAnyArgsAndReturn(ESP_OK); + + // write entry + esp_partition_write_ExpectWithArrayAndReturn(&fix.part_mock.partition, 1, 64, raw_result, 32, 32, ESP_OK); + + // write entry state + esp_partition_write_raw_ExpectAnyArgsAndReturn(ESP_OK); + + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, fix.page.state()); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.writeItem(1, nvs::ItemType::U8, "test_key", &write_data, sizeof(write_data))); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_readItem__read_entry_fails() +{ + NVSValidPageFixture fix; + + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + uint8_t read_value = 0; + + esp_partition_read_ExpectAnyArgsAndReturn(ESP_FAIL); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value)); + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state()); + + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); +} + +void test_Page_readItem__read_corrupted_entry() +{ + NVSValidPageFixture fix; + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + uint8_t read_value = 0; + + // corrupting entry + fix.value_entry[0] = 0x0; + + // first read the entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // Page::eraseEntryAndSpan() reads entry again + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // erasing entry by setting bit in entry table (0xfa -> 0xf2) + uint8_t raw_result [4] = {0xf2, 0x00, 0x00, 0x00}; + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, 1, 32, raw_result, 4, 4, ESP_OK); + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value)); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + TEST_ASSERT_EQUAL(1, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(123, fix.page.getErasedEntryCount()); +} + +void test_Page_readItem__read_corrupted_second_read_fail() +{ + NVSValidPageFixture fix; + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + uint8_t read_value = 0; + + // corrupting entry + fix.value_entry[0] = 0x0; + + // first read the entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // Page::eraseEntryAndSpan() reads entry again + esp_partition_read_ExpectAnyArgsAndReturn(ESP_FAIL); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value)); + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state()); +} + +void test_Page_readItem__read_corrupted_erase_fail() +{ + NVSValidPageFixture fix; + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + uint8_t read_value = 0; + + // corrupting entry + fix.value_entry[0] = 0x0; + + // first read the entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // Page::eraseEntryAndSpan() reads entry again + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // erasing entry by setting bit in entry table (0xfa -> 0xf2) + uint8_t raw_result [4] = {0xf2, 0x00, 0x00, 0x00}; + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, 1, 32, raw_result, 4, 4, ESP_FAIL); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value)); + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state()); +} + +void test_Page_readItem__read_entry_suceeds() +{ + NVSValidPageFixture fix; + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + uint8_t read_value = 0; + + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value)); + + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(47, read_value); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_readItem__blob_read_data_fails() +{ + NVSValidBlobPageFixture fix; + TEST_ASSERT_EQUAL(4, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(0, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + uint8_t chunk_start = 0; + uint8_t read_data [32]; + + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_entry, 32); + esp_partition_read_ExpectAnyArgsAndReturn(ESP_FAIL); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX, + ItemType::BLOB_DATA, + "test_blob", + read_data, + 32, + chunk_start)); + + TEST_ASSERT_EQUAL(4, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(0, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_readItem__corrupt_data_erase_failure() +{ + NVSValidBlobPageFixture fix; + TEST_ASSERT_EQUAL(4, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(0, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + uint8_t chunk_start = 0; + uint8_t read_data [32]; + + fix.blob_data[16] = 0xdf; + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_entry, 32); + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_data, fix.BLOB_DATA_SIZE); + + // Page::eraseEntryAndSpan() reads entry again + esp_partition_read_ExpectAnyArgsAndReturn(ESP_FAIL); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_data, fix.BLOB_DATA_SIZE); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX, + ItemType::BLOB_DATA, + "test_blob", + read_data, + 32, + chunk_start)); + + TEST_ASSERT_EQUAL(4, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(0, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_readItem__blob_corrupt_data() +{ + NVSValidBlobPageFixture fix; + TEST_ASSERT_EQUAL(4, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(0, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + uint8_t chunk_start = 0; + uint8_t read_data [32]; + + fix.blob_data[16] = 0xdf; + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_entry, 32); + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_data, fix.BLOB_DATA_SIZE); + + // Page::eraseEntryAndSpan() reads entry again + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_data, fix.BLOB_DATA_SIZE); + + // erasing entry by setting bit in entry table (0xfa -> 0xf2) + uint8_t raw_result [4] = {0xa2, 0xff, 0xff, 0xff}; + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, 1, 32, raw_result, 4, 4, ESP_OK); + + esp_partition_erase_range_ExpectAndReturn(&fix.part_mock.partition, 96, 64, ESP_OK); + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.readItem(NVSValidPageFixture::NS_INDEX, + ItemType::BLOB_DATA, + "test_blob", + read_data, + 32, + chunk_start)); + + TEST_ASSERT_EQUAL(3, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(1, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_readItem__blob_read_entry_suceeds() +{ + NVSValidBlobPageFixture fix; + TEST_ASSERT_EQUAL(4, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(0, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + uint8_t chunk_start = 0; + uint8_t read_data [32]; + + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_entry, 32); + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_data, fix.BLOB_DATA_SIZE); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.readItem(NVSValidPageFixture::NS_INDEX, + ItemType::BLOB_DATA, + "test_blob", + read_data, + 32, + chunk_start)); + + TEST_ASSERT_EQUAL_MEMORY(fix.blob_data, read_data, fix.BLOB_DATA_SIZE); + + // make sure nothing was erased, i.e. checksums matched + TEST_ASSERT_EQUAL(4, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(0, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_cmp__uninitialized() +{ + Page page; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_STATE, page.cmpItem(uint8_t(1) , "test", 47)); +} + +void test_Page_cmp__item_not_found() +{ + NVSValidPageFixture fix; + + // no expectations here since comparison uses the item hash list + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.cmpItem(uint8_t(1), "different", 47)); +} + +void test_Page_cmp__item_type_mismatch() +{ + NVSValidPageFixture fix; + + // read normal entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_TYPE_MISMATCH, fix.page.cmpItem(uint8_t(1), "test_value", int(47))); +} + +void test_Page_cmp__item_content_mismatch() +{ + NVSValidPageFixture fix; + + // read normal entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_CONTENT_DIFFERS, fix.page.cmpItem(uint8_t(1), "test_value", uint8_t(46))); +} + +void test_Page_cmp__item_content_match() +{ + NVSValidPageFixture fix; + + // read normal entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.cmpItem(NVSValidPageFixture::NS_INDEX, "test_value", uint8_t(47))); +} + +void test_Page_cmpItem__blob_data_mismatch() +{ + NVSValidBlobPageFixture fix; + + // read blob entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_entry, 32); + + // read blob data + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_data, fix.BLOB_DATA_SIZE); + + + uint8_t blob_data_different [] = + {0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xee}; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_CONTENT_DIFFERS, + fix.page.cmpItem(uint8_t(1), + ItemType::BLOB_DATA, + "test_blob", + blob_data_different, + 32)); +} + +void test_Page_cmpItem__blob_data_match() +{ + NVSValidBlobPageFixture fix; + + // read blob entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_entry, 32); + + // read blob data + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_data, fix.BLOB_DATA_SIZE); + + + uint8_t blob_data_same [] = + {0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}; + + TEST_ASSERT_EQUAL(ESP_OK, + fix.page.cmpItem(NVSValidPageFixture::NS_INDEX, + ItemType::BLOB_DATA, + "test_blob", + blob_data_same, + 32)); +} + +void test_Page_eraseItem__uninitialized() +{ + Page page; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, page.eraseItem(NVSValidPageFixture::NS_INDEX, "test_value")); +} + +void test_Page_eraseItem__key_not_found() +{ + NVSValidPageFixture fix; + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.eraseItem(NVSValidPageFixture::NS_INDEX, "different")); + + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_eraseItem__write_fail() +{ + NVSValidPageFixture fix; + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + // first read the entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // Page::eraseEntryAndSpan() reads entry again + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // erasing entry by setting bit in entry table (0xfa -> 0xf2) + uint8_t raw_result [4] = {0xf2, 0x00, 0x00, 0x00}; + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, 1, 32, raw_result, 4, 4, ESP_FAIL); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.eraseItem(NVSValidPageFixture::NS_INDEX, "test_value")); + + TEST_ASSERT_EQUAL(1, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(123, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state()); +} + +void test_Page_eraseItem__write_succeed() +{ + NVSValidPageFixture fix; + TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); + + // first read the entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // Page::eraseEntryAndSpan() reads entry again + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + // erasing entry by setting bit in entry table (0xfa -> 0xf2) + uint8_t raw_result [4] = {0xf2, 0x00, 0x00, 0x00}; + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, 1, 32, raw_result, 4, 4, ESP_OK); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.eraseItem(NVSValidPageFixture::NS_INDEX, "test_value")); + + TEST_ASSERT_EQUAL(1, fix.page.getUsedEntryCount()); + TEST_ASSERT_EQUAL(123, fix.page.getErasedEntryCount()); + + TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state()); +} + +void test_Page_findItem__uninitialized() +{ + Page page; + + size_t index = 0; + Item item; + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, + page.findItem(NVSValidPageFixture::NS_INDEX, nvs::ItemType::U8, "test_value", index, item)); +} + +void test_Page_find__wrong_ns() +{ + NVSValidPageFixture fix; + size_t index = 0; + Item item; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, + fix.page.findItem(NVSValidPageFixture::NS_INDEX + 1, nvs::ItemType::U8, "test_value", index, item)); +} + +void test_Page_find__wrong_type() +{ + NVSValidPageFixture fix; + size_t index = 0; + Item item; + + // read normal entry + esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32); + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_TYPE_MISMATCH, + fix.page.findItem(NVSValidPageFixture::NS_INDEX, nvs::ItemType::I8, "test_value", index, item)); +} + +void test_Page_find__key_empty() +{ + NVSValidPageFixture fix; + size_t index = 0; + Item item; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, + fix.page.findItem(NVSValidPageFixture::NS_INDEX, nvs::ItemType::U8, "", index, item)); +} + +void test_Page_find__wrong_key() +{ + NVSValidPageFixture fix; + size_t index = 0; + Item item; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, + fix.page.findItem(NVSValidPageFixture::NS_INDEX, nvs::ItemType::U8, "different", index, item)); +} + +void test_Page_find__too_large_index() +{ + NVSValidPageFixture fix; + size_t index = 2; + Item item; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, + fix.page.findItem(NVSValidPageFixture::NS_INDEX, nvs::ItemType::U8, "test_value", index, item)); +} + +void test_Page_findItem__without_read() +{ + NVSValidPageFixture fix; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, + fix.page.findItem(NVSValidPageFixture::NS_INDEX, nvs::ItemType::U8, "different")); +} + +void test_Page_markFull__wrong_state() +{ + NVSPageFixture fix; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_STATE, fix.page.markFull()); + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, fix.page.state()); +} + +void test_Page_markFull__success() +{ + NVSValidPageFixture fix; + Page::PageState expected_state = Page::PageState::FULL; + + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, sizeof(fix.part_mock.partition), 0, &expected_state, sizeof(expected_state), 4, ESP_OK); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.markFull()); + TEST_ASSERT_EQUAL(Page::PageState::FULL, fix.page.state()); +} + +void test_Page_markFull__write_fail() +{ + NVSValidPageFixture fix; + Page::PageState expected_state = Page::PageState::FREEING; + + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, sizeof(fix.part_mock.partition), 0, &expected_state, sizeof(expected_state), 4, ESP_FAIL); + + TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.markFreeing()); + TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state()); +} + +void test_Page_markFreeing__wrong_state() +{ + NVSPageFixture fix; + + TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_STATE, fix.page.markFreeing()); + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, fix.page.state()); +} + +void test_Page_markFreeing__success() +{ + NVSValidPageFixture fix; + Page::PageState expected_state = Page::PageState::FREEING; + + esp_partition_write_raw_ExpectWithArrayAndReturn(&fix.part_mock.partition, sizeof(fix.part_mock.partition), 0, &expected_state, sizeof(expected_state), 4, ESP_OK); + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.markFreeing()); + TEST_ASSERT_EQUAL(Page::PageState::FREEING, fix.page.state()); +} + +void test_Page_getVarDataTailroom__uninitialized_page() +{ + NVSPageFixture fix; + + TEST_ASSERT_EQUAL(Page::CHUNK_MAX_SIZE, fix.page.getVarDataTailroom()); +} + +void test_Page_getVarDataTailroom__success() +{ + NVSValidPageFixture fix; + + // blob data item, written namespace item, written normal item: 3 items + TEST_ASSERT_EQUAL((Page::ENTRY_COUNT - 3) * Page::ENTRY_SIZE, fix.page.getVarDataTailroom()); +} + +void test_Page_calcEntries__uninit() +{ + NVSPageFixture fix; + TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, fix.page.state()); + + nvs_stats_t nvsStats = {0, 0, 0, 0}; + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.calcEntries(nvsStats)); + TEST_ASSERT_EQUAL(0, nvsStats.used_entries); + TEST_ASSERT_EQUAL(Page::ENTRY_COUNT, nvsStats.free_entries); + TEST_ASSERT_EQUAL(Page::ENTRY_COUNT, nvsStats.total_entries); + TEST_ASSERT_EQUAL(0, nvsStats.namespace_count); +} + +void test_Page_calcEntries__corrupt() +{ + PartitionMockFixture fix; + Page page; + + uint8_t raw_header_corrupt [] = {0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x16, 0xdd, 0xdc}; + + esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); + esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header_corrupt, 32); + + // Page::load() should return ESP_OK, but state has to be corrupt + TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part_mock, 0)); + + TEST_ASSERT_EQUAL(Page::PageState::CORRUPT, page.state()); + + nvs_stats_t nvsStats = {0, 0, 0, 0}; + + TEST_ASSERT_EQUAL(ESP_OK, page.calcEntries(nvsStats)); + TEST_ASSERT_EQUAL(0, nvsStats.used_entries); + TEST_ASSERT_EQUAL(Page::ENTRY_COUNT, nvsStats.free_entries); + TEST_ASSERT_EQUAL(Page::ENTRY_COUNT, nvsStats.total_entries); + TEST_ASSERT_EQUAL(0, nvsStats.namespace_count); +} + +void test_Page_calcEntries__active_wo_blob() +{ + NVSValidPageFixture fix; + + nvs_stats_t nvsStats = {0, 0, 0, 0}; + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.calcEntries(nvsStats)); + TEST_ASSERT_EQUAL(2, nvsStats.used_entries); + TEST_ASSERT_EQUAL(124, nvsStats.free_entries); + TEST_ASSERT_EQUAL(Page::ENTRY_COUNT, nvsStats.total_entries); + TEST_ASSERT_EQUAL(0, nvsStats.namespace_count); +} + +void test_Page_calcEntries__active_with_blob() +{ + NVSValidBlobPageFixture fix; + + nvs_stats_t nvsStats = {0, 0, 0, 0}; + + TEST_ASSERT_EQUAL(ESP_OK, fix.page.calcEntries(nvsStats)); + TEST_ASSERT_EQUAL(4, nvsStats.used_entries); + TEST_ASSERT_EQUAL(122, nvsStats.free_entries); + TEST_ASSERT_EQUAL(Page::ENTRY_COUNT, nvsStats.total_entries); + TEST_ASSERT_EQUAL(0, nvsStats.namespace_count); +} + +void test_Page_calcEntries__invalid() +{ + Page page; + + nvs_stats_t nvsStats = {0, 0, 0, 0}; + + TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state()); + + // total entries always get updated + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, page.calcEntries(nvsStats)); + TEST_ASSERT_EQUAL(0, nvsStats.used_entries); + TEST_ASSERT_EQUAL(0, nvsStats.free_entries); + TEST_ASSERT_EQUAL(Page::ENTRY_COUNT, nvsStats.total_entries); + TEST_ASSERT_EQUAL(0, nvsStats.namespace_count); +} + +int main(int argc, char **argv) +{ + UNITY_BEGIN(); + RUN_TEST(test_Page_load_reading_header_fails); + RUN_TEST(test_Page_load_reading_data_fails); + RUN_TEST(test_Page_load__uninitialized_page_has_0xfe); + RUN_TEST(test_Page_load__initialized_corrupt_header); + RUN_TEST(test_Page_load_success); + RUN_TEST(test_Page_load_full_page); + RUN_TEST(test_Page_load__seq_number_0); + RUN_TEST(test_Page_erase__write_fail); + RUN_TEST(test_Page_erase__success); + RUN_TEST(test_Page_write__initialize_write_failure); + RUN_TEST(test_Page_write__write_data_fails); + RUN_TEST(test_page_write__write_correct_entry_state); + RUN_TEST(test_Page_write__write_correct_data); + RUN_TEST(test_Page_readItem__read_entry_fails); + RUN_TEST(test_Page_readItem__read_corrupted_entry); + RUN_TEST(test_Page_readItem__read_corrupted_second_read_fail); + RUN_TEST(test_Page_readItem__read_corrupted_erase_fail); + RUN_TEST(test_Page_readItem__read_entry_suceeds); + RUN_TEST(test_Page_readItem__blob_read_data_fails); + RUN_TEST(test_Page_readItem__blob_corrupt_data); + RUN_TEST(test_Page_readItem__blob_read_entry_suceeds); + RUN_TEST(test_Page_cmp__uninitialized); + RUN_TEST(test_Page_cmp__item_not_found); + RUN_TEST(test_Page_cmp__item_type_mismatch); + RUN_TEST(test_Page_cmp__item_content_mismatch); + RUN_TEST(test_Page_cmp__item_content_match); + RUN_TEST(test_Page_cmpItem__blob_data_mismatch); + RUN_TEST(test_Page_cmpItem__blob_data_match); + RUN_TEST(test_Page_eraseItem__uninitialized); + RUN_TEST(test_Page_eraseItem__key_not_found); + RUN_TEST(test_Page_eraseItem__write_fail); + RUN_TEST(test_Page_readItem__corrupt_data_erase_failure); + RUN_TEST(test_Page_eraseItem__write_succeed); + RUN_TEST(test_Page_findItem__uninitialized); + RUN_TEST(test_Page_find__wrong_ns); + RUN_TEST(test_Page_find__wrong_type); + RUN_TEST(test_Page_find__key_empty); + RUN_TEST(test_Page_find__wrong_key); + RUN_TEST(test_Page_find__too_large_index); + RUN_TEST(test_Page_findItem__without_read); + RUN_TEST(test_Page_markFull__wrong_state); + RUN_TEST(test_Page_markFreeing__wrong_state); + RUN_TEST(test_Page_markFull__success); + RUN_TEST(test_Page_markFreeing__success); + RUN_TEST(test_Page_markFull__write_fail); + RUN_TEST(test_Page_getVarDataTailroom__uninitialized_page); + RUN_TEST(test_Page_getVarDataTailroom__success); + RUN_TEST(test_Page_calcEntries__uninit); + RUN_TEST(test_Page_calcEntries__corrupt); + RUN_TEST(test_Page_calcEntries__active_wo_blob); + RUN_TEST(test_Page_calcEntries__active_with_blob); + RUN_TEST(test_Page_calcEntries__invalid); + UNITY_END(); + return 0; +} diff --git a/components/nvs_flash/host_test/nvs_page_test/sdkconfig.defaults b/components/nvs_flash/host_test/nvs_page_test/sdkconfig.defaults new file mode 100644 index 0000000000..a057733348 --- /dev/null +++ b/components/nvs_flash/host_test/nvs_page_test/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n +CONFIG_IDF_TARGET="linux" +CONFIG_CXX_EXCEPTIONS=y diff --git a/components/nvs_flash/test_nvs_host/crc.cpp b/components/nvs_flash/mock/int/crc.cpp similarity index 97% rename from components/nvs_flash/test_nvs_host/crc.cpp rename to components/nvs_flash/mock/int/crc.cpp index 49980776c3..5c1b745648 100644 --- a/components/nvs_flash/test_nvs_host/crc.cpp +++ b/components/nvs_flash/mock/int/crc.cpp @@ -52,7 +52,7 @@ static const unsigned int crc32_le_table[256] = { -extern "C" unsigned int crc32_le(unsigned int crc, unsigned char const * buf,unsigned int len) +extern "C" uint32_t esp_rom_crc32_le(unsigned int crc, unsigned char const * buf,unsigned int len) { unsigned int i; crc = ~crc; diff --git a/components/nvs_flash/test_nvs_host/crc.h b/components/nvs_flash/mock/int/crc.h similarity index 72% rename from components/nvs_flash/test_nvs_host/crc.h rename to components/nvs_flash/mock/int/crc.h index c752b30303..f675da3271 100644 --- a/components/nvs_flash/test_nvs_host/crc.h +++ b/components/nvs_flash/mock/int/crc.h @@ -20,7 +20,11 @@ extern "C" { #endif -uint32_t crc32_le(uint32_t crc, const uint8_t* buf, size_t len); +/** + * Mock function to replace ESP ROM function used in IDF with a Linux implementation. + * Note: the name MUST have the prefix esp_rom_* since tools/ci/check_rom_apis.sh checks and complains otherwise. + */ +uint32_t esp_rom_crc32_le(uint32_t crc, const uint8_t* buf, size_t len); #ifdef __cplusplus } diff --git a/components/nvs_flash/src/compressed_enum_table.hpp b/components/nvs_flash/src/compressed_enum_table.hpp index 319d86a45c..dcf9d09993 100644 --- a/components/nvs_flash/src/compressed_enum_table.hpp +++ b/components/nvs_flash/src/compressed_enum_table.hpp @@ -35,7 +35,7 @@ public: Tenum get(size_t index) const { - assert(index >= 0 && index < Nitems); + assert(index < Nitems); size_t wordIndex = index / ITEMS_PER_WORD; size_t offset = (index % ITEMS_PER_WORD) * Nbits; @@ -44,7 +44,7 @@ public: void set(size_t index, Tenum val) { - assert(index >= 0 && index < Nitems); + assert(index < Nitems); size_t wordIndex = index / ITEMS_PER_WORD; size_t offset = (index % ITEMS_PER_WORD) * Nbits; diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 423e1fdceb..008ff463eb 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -22,17 +22,17 @@ #include #include "nvs_handle_simple.hpp" -#ifdef ESP_PLATFORM +#ifdef LINUX_TARGET +#include "crc.h" +#define ESP_LOGD(...) +#else // LINUX_TARGET #include // Uncomment this line to force output from this module // #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include "esp_log.h" static const char* TAG = "nvs"; -#else -#include "crc.h" -#define ESP_LOGD(...) -#endif +#endif // ! LINUX_TARGET class NVSHandleEntry : public intrusive_list_node { public: @@ -56,9 +56,9 @@ uint32_t NVSHandleEntry::s_nvs_next_handle; extern "C" void nvs_dump(const char *partName); -#ifdef ESP_PLATFORM +#ifndef LINUX_TARGET SemaphoreHandle_t nvs::Lock::mSemaphore = nullptr; -#endif +#endif // ! LINUX_TARGET using namespace std; using namespace nvs; @@ -125,7 +125,7 @@ extern "C" esp_err_t nvs_flash_init_partition_ptr(const esp_partition_t *partiti return init_res; } -#ifdef ESP_PLATFORM +#ifndef LINUX_TARGET extern "C" esp_err_t nvs_flash_init_partition(const char *part_name) { Lock::init(); @@ -204,7 +204,7 @@ extern "C" esp_err_t nvs_flash_erase(void) { return nvs_flash_erase_partition(NVS_DEFAULT_PART_NAME); } -#endif // ESP_PLATFORM +#endif // ! LINUX_TARGET extern "C" esp_err_t nvs_flash_deinit_partition(const char* partition_name) { @@ -528,7 +528,7 @@ extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle_t c_handle, size_t* use return err; } -#if (defined CONFIG_NVS_ENCRYPTION) && (defined ESP_PLATFORM) +#if (defined CONFIG_NVS_ENCRYPTION) && (!defined LINUX_TARGET) extern "C" esp_err_t nvs_flash_generate_keys(const esp_partition_t* partition, nvs_sec_cfg_t* cfg) { diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index 0563eca2b8..2f72a2090d 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "nvs_page.hpp" -#if defined(ESP_PLATFORM) -#include -#else +#if defined(LINUX_TARGET) #include "crc.h" +#else +#include #endif #include #include @@ -27,7 +27,7 @@ Page::Page() : mPartition(nullptr) { } uint32_t Page::Header::calculateCrc32() { - return crc32_le(0xffffffff, + return esp_rom_crc32_le(0xffffffff, reinterpret_cast(this) + offsetof(Header, mSeqNumber), offsetof(Header, mCrc32) - offsetof(Header, mSeqNumber)); } @@ -137,7 +137,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) const uint8_t* buf = data; -#ifdef ESP_PLATFORM +#if !defined LINUX_TARGET // TODO: check whether still necessary with esp_partition* API /* On the ESP32, data can come from DROM, which is not accessible by spi_flash_write * function. To work around this, we copy the data to heap if it came from DROM. @@ -153,15 +153,15 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) } memcpy((void*)buf, data, size); } -#endif //ESP_PLATFORM +#endif // ! LINUX_TARGET auto rc = mPartition->write(getEntryAddress(mNextFreeEntry), buf, size); -#ifdef ESP_PLATFORM +#if !defined LINUX_TARGET if (buf != data) { free((void*)buf); } -#endif //ESP_PLATFORM +#endif // ! LINUX_TARGET if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -968,7 +968,7 @@ size_t Page::getVarDataTailroom() const } else if (mState == PageState::FULL) { return 0; } - /* Skip one entry for header*/ + /* Skip one entry for blob data item precessing the data */ return ((mNextFreeEntry < (ENTRY_COUNT-1)) ? ((ENTRY_COUNT - mNextFreeEntry - 1) * ENTRY_SIZE): 0); } diff --git a/components/nvs_flash/src/nvs_page.hpp b/components/nvs_flash/src/nvs_page.hpp index bb6a0dca62..5857f1ffe4 100644 --- a/components/nvs_flash/src/nvs_page.hpp +++ b/components/nvs_flash/src/nvs_page.hpp @@ -224,6 +224,9 @@ protected: uint16_t mUsedEntryCount = 0; uint16_t mErasedEntryCount = 0; + /** + * This hash list stores hashes of namespace index, key, and ChunkIndex for quick lookup when searching items. + */ HashList mHashList; Partition *mPartition; diff --git a/components/nvs_flash/src/nvs_partition.cpp b/components/nvs_flash/src/nvs_partition.cpp index 370316af08..f1b7d36d2d 100644 --- a/components/nvs_flash/src/nvs_partition.cpp +++ b/components/nvs_flash/src/nvs_partition.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "string.h" +#include #include "nvs_partition.hpp" namespace nvs { @@ -22,7 +22,7 @@ NVSPartition::NVSPartition(const esp_partition_t* partition) { // ensure the class is in a valid state if (partition == nullptr) { - abort(); + std::abort(); } } diff --git a/components/nvs_flash/src/nvs_platform.hpp b/components/nvs_flash/src/nvs_platform.hpp index 0973c4875c..5c6b5b8b23 100644 --- a/components/nvs_flash/src/nvs_platform.hpp +++ b/components/nvs_flash/src/nvs_platform.hpp @@ -11,11 +11,23 @@ // 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. -#ifndef nvs_platform_h -#define nvs_platform_h +#pragma once +#ifdef LINUX_TARGET +namespace nvs +{ +class Lock +{ +public: + Lock() { } + ~Lock() { } + static void init() {} + static void uninit() {} +}; +} // namespace nvs + +#else // LINUX_TARGET -#ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -63,19 +75,4 @@ public: }; } // namespace nvs -#else // ESP_PLATFORM -namespace nvs -{ -class Lock -{ -public: - Lock() { } - ~Lock() { } - static void init() {} - static void uninit() {} -}; -} // namespace nvs -#endif // ESP_PLATFORM - - -#endif /* nvs_platform_h */ +#endif // LINUX_TARGET diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index 88176c1045..52952b7a38 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -14,9 +14,15 @@ #include "nvs_storage.hpp" #ifndef ESP_PLATFORM +// We need NO_DEBUG_STORAGE here since the integration tests on the host add some debug code. +// The unit tests, however, don't want debug code since they check the behavior via data in/output and disturb +// the order of calling mocked functions. +#ifndef NO_DEBUG_STORAGE #include #include +#define DEBUG_STORAGE #endif +#endif // !ESP_PLATFORM namespace nvs { @@ -136,7 +142,7 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) // Purge the blob index list blobIdxList.clearAndFreeNodes(); -#ifndef ESP_PLATFORM +#ifdef DEBUG_STORAGE debugCheck(); #endif return ESP_OK; @@ -165,7 +171,7 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo uint8_t chunkCount = 0; TUsedPageList usedPages; size_t remainingSize = dataSize; - size_t offset=0; + size_t offset = 0; esp_err_t err = ESP_OK; /* Check how much maximum data can be accommodated**/ @@ -182,8 +188,8 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo do { Page& page = getCurrentPage(); size_t tailroom = page.getVarDataTailroom(); - size_t chunkSize =0; - if (!chunkCount && tailroom < dataSize && tailroom < Page::CHUNK_MAX_SIZE/10) { + size_t chunkSize = 0; + if (chunkCount == 0U && ((tailroom < dataSize) || (tailroom == 0 && dataSize == 0)) && tailroom < Page::CHUNK_MAX_SIZE/10) { /** This is the first chunk and tailroom is too small ***/ if (page.state() != Page::PageState::FULL) { err = page.markFull(); @@ -206,7 +212,7 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo } /* Split the blob into two and store the chunk of available size onto the current page */ - assert(tailroom!=0); + assert(tailroom != 0); chunkSize = (remainingSize > tailroom)? tailroom : remainingSize; remainingSize -= chunkSize; @@ -383,7 +389,7 @@ esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key return err; } } -#ifndef ESP_PLATFORM +#ifdef DEBUG_STORAGE debugCheck(); #endif return ESP_OK; @@ -666,7 +672,7 @@ void Storage::debugDump() } } -#ifndef ESP_PLATFORM +#ifdef DEBUG_STORAGE void Storage::debugCheck() { std::map keys; @@ -691,7 +697,7 @@ void Storage::debugCheck() assert(usedCount == p->getUsedEntryCount()); } } -#endif //ESP_PLATFORM +#endif //DEBUG_STORAGE esp_err_t Storage::fillStats(nvs_stats_t& nvsStats) { diff --git a/components/nvs_flash/src/nvs_types.cpp b/components/nvs_flash/src/nvs_types.cpp index f6b4f31e7f..0189dd70b9 100644 --- a/components/nvs_flash/src/nvs_types.cpp +++ b/components/nvs_flash/src/nvs_types.cpp @@ -13,10 +13,10 @@ // limitations under the License. #include "nvs_types.hpp" -#if defined(ESP_PLATFORM) -#include -#else +#if defined(LINUX_TARGET) #include "crc.h" +#else +#include #endif namespace nvs @@ -25,10 +25,10 @@ uint32_t Item::calculateCrc32() const { uint32_t result = 0xffffffff; const uint8_t* p = reinterpret_cast(this); - result = crc32_le(result, p + offsetof(Item, nsIndex), + result = esp_rom_crc32_le(result, p + offsetof(Item, nsIndex), offsetof(Item, crc32) - offsetof(Item, nsIndex)); - result = crc32_le(result, p + offsetof(Item, key), sizeof(key)); - result = crc32_le(result, p + offsetof(Item, data), sizeof(data)); + result = esp_rom_crc32_le(result, p + offsetof(Item, key), sizeof(key)); + result = esp_rom_crc32_le(result, p + offsetof(Item, data), sizeof(data)); return result; } @@ -36,17 +36,17 @@ uint32_t Item::calculateCrc32WithoutValue() const { uint32_t result = 0xffffffff; const uint8_t* p = reinterpret_cast(this); - result = crc32_le(result, p + offsetof(Item, nsIndex), + result = esp_rom_crc32_le(result, p + offsetof(Item, nsIndex), offsetof(Item, datatype) - offsetof(Item, nsIndex)); - result = crc32_le(result, p + offsetof(Item, key), sizeof(key)); - result = crc32_le(result, p + offsetof(Item, chunkIndex), sizeof(chunkIndex)); + result = esp_rom_crc32_le(result, p + offsetof(Item, key), sizeof(key)); + result = esp_rom_crc32_le(result, p + offsetof(Item, chunkIndex), sizeof(chunkIndex)); return result; } uint32_t Item::calculateCrc32(const uint8_t* data, size_t size) { uint32_t result = 0xffffffff; - result = crc32_le(result, data, size); + result = esp_rom_crc32_le(result, data, size); return result; } diff --git a/components/nvs_flash/test_nvs_host/Makefile b/components/nvs_flash/test_nvs_host/Makefile index c04ae3a424..32964fa3e6 100644 --- a/components/nvs_flash/test_nvs_host/Makefile +++ b/components/nvs_flash/test_nvs_host/Makefile @@ -3,6 +3,7 @@ all: $(TEST_PROGRAM) SOURCE_FILES = \ esp_error_check_stub.cpp \ + ../mock/int/crc.cpp \ $(addprefix ../src/, \ nvs_types.cpp \ nvs_api.cpp \ @@ -28,7 +29,6 @@ SOURCE_FILES = \ test_nvs_partition.cpp \ test_nvs_cxx_api.cpp \ test_nvs_initialization.cpp \ - crc.cpp \ main.cpp ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1) @@ -37,9 +37,9 @@ else COMPILER := gcc endif -CPPFLAGS += -I../include -I../src -I./ -I../../esp_common/include -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../hal/include -I ../../xtensa/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage -g2 -ggdb -CFLAGS += -fprofile-arcs -ftest-coverage -CXXFLAGS += -std=c++11 -Wall -Werror +CPPFLAGS += -I../include -I../src -I../mock/int -I./ -I../../esp_common/include -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../hal/include -I ../../xtensa/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage -g2 -ggdb +CFLAGS += -fprofile-arcs -ftest-coverage -DLINUX_TARGET +CXXFLAGS += -std=c++11 -Wall -Werror -DLINUX_TARGET LDFLAGS += -lstdc++ -Wall -fprofile-arcs -ftest-coverage ifeq ($(COMPILER),clang) diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index ab0aef765f..df7b630c16 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -1,7 +1,6 @@ idf_build_get_property(spi_flash_mock CONFIG_SPI_FLASH_MOCK) idf_build_get_property(target IDF_TARGET) if(${spi_flash_mock}) - message(STATUS "building SPI FLASH MOCKS") set(IDF_PATH $ENV{IDF_PATH}) @@ -41,13 +40,20 @@ if(${spi_flash_mock}) INCLUDE_DIRS ${include_dirs} REQUIRES cmock) + add_custom_command( + OUTPUT ruby_found SYMBOLIC + COMMAND "ruby" "-v" + COMMENT "Try to find ruby. If this fails, you need to install ruby" + ) + # This command builds the mocks. # First, environment variable UNITY_DIR is set. This is necessary to prevent unity from looking in its own submodule # which doesn't work in our CI yet... # The rest is a straight forward call to cmock.rb, consult cmock's documentation for more information. add_custom_command( - OUTPUT ${MOCK_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env "UNITY_DIR=${IDF_PATH}/components/unity/unity" + OUTPUT ${MOCK_OUTPUT} + DEPENDS ruby_found + COMMAND ${CMAKE_COMMAND} -E env "UNITY_DIR=${IDF_PATH}/components/unity/unity" ruby ${CMOCK_DIR}/lib/cmock.rb -o${CMAKE_CURRENT_SOURCE_DIR}/mock/mock_config.yaml diff --git a/components/spi_flash/include/esp_spi_flash_counters.h b/components/spi_flash/include/esp_spi_flash_counters.h index 5ec3b95140..ab8157c256 100644 --- a/components/spi_flash/include/esp_spi_flash_counters.h +++ b/components/spi_flash/include/esp_spi_flash_counters.h @@ -63,4 +63,3 @@ const spi_flash_counters_t* spi_flash_get_counters(void); #endif #endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS -