NVS flash: host-based unit test of nvs::Page

* General tests like page loading from flash
* Rough test of fixed-size data types
* Rough test of blob read
* Added coverage target in cmake, also accessible
  via `idf.py coverage`
* Fixed unsigned comparison in comp. enum table
* introducing temporary LINUX_TARGET define
This commit is contained in:
Jakob Hasse
2020-11-06 15:54:51 +08:00
committed by Fu Hanxi
parent c233ce0449
commit 00819a3022
20 changed files with 1547 additions and 75 deletions

View File

@@ -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()

View File

@@ -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 <exception>
#include <string>
#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;
};

View File

@@ -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/"
)

View File

@@ -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.

View File

@@ -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)

View File

@@ -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 <stdio.h>
#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<uint8_t>(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<uint8_t>(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<uint8_t>(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<uint8_t>(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;
}

View File

@@ -0,0 +1,3 @@
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
CONFIG_IDF_TARGET="linux"
CONFIG_CXX_EXCEPTIONS=y

View File

@@ -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;

View File

@@ -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
}

View File

@@ -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;

View File

@@ -22,17 +22,17 @@
#include <functional>
#include "nvs_handle_simple.hpp"
#ifdef ESP_PLATFORM
#ifdef LINUX_TARGET
#include "crc.h"
#define ESP_LOGD(...)
#else // LINUX_TARGET
#include <esp32/rom/crc.h>
// 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<NVSHandleEntry> {
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)
{

View File

@@ -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 <esp32/rom/crc.h>
#else
#if defined(LINUX_TARGET)
#include "crc.h"
#else
#include <esp_rom_crc.h>
#endif
#include <cstdio>
#include <cstring>
@@ -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<uint8_t*>(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);
}

View File

@@ -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;

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "string.h"
#include <cstdlib>
#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();
}
}

View File

@@ -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

View File

@@ -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 <map>
#include <sstream>
#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<std::string, Page*> 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)
{

View File

@@ -13,10 +13,10 @@
// limitations under the License.
#include "nvs_types.hpp"
#if defined(ESP_PLATFORM)
#include <esp32/rom/crc.h>
#else
#if defined(LINUX_TARGET)
#include "crc.h"
#else
#include <esp_rom_crc.h>
#endif
namespace nvs
@@ -25,10 +25,10 @@ uint32_t Item::calculateCrc32() const
{
uint32_t result = 0xffffffff;
const uint8_t* p = reinterpret_cast<const uint8_t*>(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<const uint8_t*>(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;
}

View File

@@ -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)

View File

@@ -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

View File

@@ -63,4 +63,3 @@ const spi_flash_counters_t* spi_flash_get_counters(void);
#endif
#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS