Merge branch 'feature/storage_host_test_nvs_page' into 'master'

feature(nvs_flash): nvs_page host tests updated to use esp_partition for linux instead of CMock emulation of API

Closes IDF-6166

See merge request espressif/esp-idf!25978
This commit is contained in:
Radek Tandler 2023-10-14 08:41:50 +08:00
commit 4c8c5debed
6 changed files with 546 additions and 689 deletions

View File

@ -1,432 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#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() const noexcept {
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;
}
bool get_readonly() override
{
return partition.readonly;
}
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);
// This resets the mocks and prevents meeting accidental expectations from previous tests.
Mockesp_partition_Init();
}
~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 NVSValidPageFlashFixture : 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];
NVSValidPageFlashFixture(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}
{
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);
}
};
struct NVSValidPageFixture : public NVSValidPageFlashFixture {
NVSValidPageFixture(uint32_t start_sector = 0,
uint32_t sector_size = 1,
const char *partition_name = NVS_DEFAULT_PART_NAME)
: NVSValidPageFlashFixture(start_sector, sector_size, partition_name), page()
{
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()
{
// 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

@ -5,8 +5,6 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/driver/")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/spi_flash/")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/esp_partition/")
idf_build_set_property(COMPILE_DEFINITIONS "NO_DEBUG_STORAGE" APPEND)
project(test_nvs_page_host)

View File

@ -1,11 +1,13 @@
idf_component_register(SRCS "nvs_page_test.cpp"
INCLUDE_DIRS
"."
"${CMAKE_CURRENT_SOURCE_DIR}/../../fixtures"
"${CMAKE_CURRENT_SOURCE_DIR}/../../../src"
PRIV_INCLUDE_DIRS
"${CMAKE_CURRENT_SOURCE_DIR}/../../../private_include"
REQUIRES cmock nvs_flash spi_flash partition_table esp_partition)
REQUIRES nvs_flash)
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage)
target_link_libraries(${COMPONENT_LIB} --coverage)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
target_compile_options(${COMPONENT_LIB} PRIVATE -std=gnu++20)
endif()

View File

@ -3,87 +3,91 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
static const char* TAG = "nvs_page_host_test";
#include <stdio.h>
#include "unity.h"
#include "test_fixtures.hpp"
extern "C" {
#include "Mockesp_partition.h"
}
#include "esp_log.h"
using namespace std;
using namespace nvs;
void test_Page_load_reading_header_fails()
{
PartitionMock mock(0, 4096);
PartitionEmulationFixture fix;
/*
esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_ERR_INVALID_ARG);
*/
/*
At the moment we cannot simulate esp_partition_read_raw call failure
*/
Page page;
TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state());
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, page.load(&mock, 0));
Mockesp_partition_Verify();
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, page.load(&fix.part, 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);
PartitionEmulationFixture fix;
fix.erase_all();
fix.write_raw(0, header, sizeof(header));
/*
esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK);
esp_partition_read_raw_ReturnArrayThruPtr_dst(header, 32);
esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_FAIL);
*/
/*
At the moment we cannot simulate esp_partition_read_raw call failure
*/
Page page;
TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state());
TEST_ASSERT_EQUAL(ESP_FAIL, page.load(&mock, 0));
Mockesp_partition_Verify();
TEST_ASSERT_EQUAL(ESP_FAIL, page.load(&fix.part, 0));
}
void test_Page_load__uninitialized_page_has_0xfe()
{
PartitionMockFixture fix;
PartitionEmulationFixture 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);
uint8_t uninitialized_symbol = 0xfe;
fix.write_raw(511, &uninitialized_symbol, sizeof(uninitialized_symbol));
// 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(ESP_OK, page.load(&fix.part, 0));
TEST_ASSERT_EQUAL(Page::PageState::CORRUPT, page.state());
Mockesp_partition_Verify();
}
void test_Page_load__initialized_corrupt_header()
{
PartitionMockFixture fix;
PartitionEmulationFixture 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);
fix.write_raw(0, raw_header_corrupt, sizeof(raw_header_corrupt));
// 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(ESP_OK, page.load(&fix.part, 0));
TEST_ASSERT_EQUAL(Page::PageState::CORRUPT, page.state());
Mockesp_partition_Verify();
}
void test_Page_load__corrupt_entry_table()
{
PartitionMockFixture fix;
PartitionEmulationFixture fix;
// valid header
uint8_t raw_header_valid [32] = {0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@ -96,67 +100,88 @@ void test_Page_load__corrupt_entry_table()
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
uint8_t raw_header[4] = {0xff, 0xff, 0xff, 0xff};
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);
// we expect a raw word write from the partition in order to change the entry bits to erased (0)
esp_partition_write_raw_ExpectAndReturn(&fix.part_mock.partition, 32, nullptr, 4, ESP_OK);
esp_partition_write_raw_IgnoreArg_src();
// corrupt entry table as well as crc of corresponding item
raw_entry_table[0] = 0xf6;
// Expected sequence of data blocks read by page.load
fix.write_raw( 0, raw_header_valid, sizeof(raw_header_valid));
fix.write_raw( 32, raw_entry_table, sizeof(raw_entry_table));
fix.write_raw(128, raw_header, sizeof(raw_header));
fix.write_raw( 64, ns_entry, sizeof(ns_entry));
Page page;
// 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(ESP_OK, page.load(&fix.part, 0));
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, page.state());
TEST_ASSERT_EQUAL(1, page.getUsedEntryCount());
Mockesp_partition_Verify();
}
void test_Page_load_success()
{
PartitionMockFixture fix;
esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK);
esp_partition_read_raw_ReturnArrayThruPtr_dst(fix.raw_header, 32);
PartitionEmulationFixture fix;
const size_t nvs_page_size = 512;
const size_t nvs_header_size = 32;
uint8_t raw_header[nvs_page_size];
std::fill_n(raw_header, sizeof(raw_header)/sizeof(raw_header[0]), UINT8_MAX);
// Expected sequence of data blocks read by page.load
fix.write_raw(0, raw_header, nvs_header_size);
for (int i = 0; i < 8; i++) {
esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK);
esp_partition_read_raw_ReturnArrayThruPtr_dst(fix.raw_header, 512);
fix.write_raw(i * nvs_page_size, raw_header, sizeof(raw_header));
}
Page page;
TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state());
TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part_mock, 0));
TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part, 0));
TEST_ASSERT_EQUAL(Page::PageState::UNINITIALIZED, page.state());
}
void test_Page_load_full_page()
{
NVSFullPageFixture fix(0, 1, NVS_DEFAULT_PART_NAME, false);
PartitionEmulationFixture fix;
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());
// valid header
uint8_t raw_header_valid [32] = {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};
// entry table with just one entry free
uint8_t raw_entry_table [32];
// namespace entry
uint8_t ns_entry [32] = {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};
// one value entry
uint8_t value_entry [32] = {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};
// 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;
// Expected sequence of data blocks read by page.load
fix.write_raw( 0, raw_header_valid, sizeof(raw_header_valid));
fix.write_raw( 32, raw_entry_table, sizeof(raw_entry_table));
fix.write_raw( 64, ns_entry, sizeof(ns_entry));
fix.write_raw( 96, value_entry, sizeof(value_entry));
Page page;
TEST_ASSERT_EQUAL(Page::PageState::INVALID, page.state());
TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part, 0));
TEST_ASSERT_EQUAL(Page::PageState::FULL, page.state());
}
void test_Page_load__seq_number_0()
{
NVSValidPageFixture fix;
@ -170,9 +195,10 @@ void test_Page_erase__write_fail()
{
NVSValidPageFixture fix;
esp_partition_erase_range_ExpectAndReturn(&fix.part_mock.partition, 0, 4096, ESP_FAIL);
// fail at 1st attempt to call esp_partition_erase_range
fix.fail_erase_at(1);
TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.erase());
TEST_ASSERT_EQUAL(ESP_ERR_FLASH_OP_FAIL, fix.page.erase());
TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state());
}
@ -180,44 +206,44 @@ 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;
PartitionEmulationFixture fix;
// Make partition empty - uninitialized
fix.erase_all();
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);
// Emulate failed attempt to write data to the flash after writeItem is called
fix.fail_write_at(1);
Page page;
TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part_mock, 0));
TEST_ASSERT_EQUAL(ESP_OK, page.load(&fix.part, 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(ESP_ERR_FLASH_OP_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);
// It is expected, that 2 write calls will be made
// Emulate failed second attempt to write data to the flash after writeItem is called
fix.fail_write_at(2);
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(ESP_ERR_FLASH_OP_FAIL, fix.page.writeItem(1, nvs::ItemType::U8, "test", &write_data, sizeof(write_data)));
TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state());
}
@ -231,19 +257,10 @@ void test_page_write__write_correct_entry_state()
// 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 whether partition memory space contains indication of first entry written
TEST_ASSERT_EQUAL(ESP_OK, fix.compare_raw(32, raw_result, sizeof(raw_result)));
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state());
}
@ -254,19 +271,10 @@ void test_Page_write__write_correct_data()
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 whether partition memory space contains value entry
TEST_ASSERT_EQUAL(ESP_OK, fix.compare_raw(64, raw_result, sizeof(raw_result)));
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state());
}
@ -280,9 +288,13 @@ void test_Page_readItem__read_entry_fails()
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state());
uint8_t read_value = 0;
/*
esp_partition_read_ExpectAnyArgsAndReturn(ESP_FAIL);
*/
/*
At the moment we do not have simulation of failed flash read
*/
TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value));
TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state());
@ -301,25 +313,15 @@ void test_Page_readItem__read_corrupted_entry()
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);
// prepare corrupted entry in flash memory
uint8_t corrupted_value_entry = 0x0;
fix.write_raw( 96, &corrupted_value_entry, sizeof(corrupted_value_entry));
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value));
// check that repair mechanism of corrupted entry caused erasing entry by setting bit in entry table (0xfa -> 0xf2)
uint8_t raw_result [4] = {0xf2, 0x00, 0x00, 0x00};
TEST_ASSERT_EQUAL(ESP_OK, fix.compare_raw(32, raw_result, sizeof(raw_result)));
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state());
TEST_ASSERT_EQUAL(1, fix.page.getUsedEntryCount());
TEST_ASSERT_EQUAL(123, fix.page.getErasedEntryCount());
}
@ -336,13 +338,21 @@ void test_Page_readItem__read_corrupted_second_read_fail()
// corrupting entry
fix.value_entry[0] = 0x0;
fix.write_raw(96, fix.value_entry, sizeof(fix.value_entry));
/*
// 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);
*/
/*
It is expected that readItem will call esp_partition_read function twice
We cannot simulate the second read attempt failure at the moment
*/
TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value));
@ -361,20 +371,14 @@ void test_Page_readItem__read_corrupted_erase_fail()
// corrupting entry
fix.value_entry[0] = 0x0;
// prepare corrupt entry for reading
fix.write_raw( 96, fix.value_entry, sizeof(fix.value_entry));
// first read the entry
esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK);
esp_partition_read_ReturnArrayThruPtr_dst(fix.value_entry, 32);
// emulate write failure as nvs will try to invalidate the corupt entry
// by setting bit in entry table (0xfa -> 0xf2)
fix.fail_write_at(1);
// 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(ESP_ERR_FLASH_OP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX, "test_value", read_value));
TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state());
}
@ -389,9 +393,6 @@ void test_Page_readItem__read_entry_suceeds()
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());
@ -413,9 +414,17 @@ void test_Page_readItem__blob_read_data_fails()
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);
*/
/*
Disabled. For now, we are unable to simulate read attempt failures
readItem will read from index 96 in first call and from offset 128 in the second call.
The second call should fail
*/
TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX,
ItemType::BLOB_DATA,
@ -441,7 +450,11 @@ void test_Page_readItem__corrupt_data_erase_failure()
uint8_t chunk_start = 0;
uint8_t read_data [32];
// corupt the data
fix.blob_data[16] = 0xdf;
fix.write_raw(128, fix.blob_data, sizeof(fix.blob_data));
/*
esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK);
esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_entry, 32);
esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK);
@ -450,6 +463,15 @@ void test_Page_readItem__corrupt_data_erase_failure()
// Page::eraseEntryAndSpan() reads entry again
esp_partition_read_ExpectAnyArgsAndReturn(ESP_FAIL);
esp_partition_read_ReturnArrayThruPtr_dst(fix.blob_data, fix.BLOB_DATA_SIZE);
*/
/*
We cannot migrate the test case at the moment as we are not able to simulate read failure
fix.page.readItem will:
esp_partition_read at offset 96
esp_partition_read at offset 128
esp_partition_read at offset 96, which should fail
*/
TEST_ASSERT_EQUAL(ESP_FAIL, fix.page.readItem(NVSValidPageFixture::NS_INDEX,
ItemType::BLOB_DATA,
@ -475,22 +497,11 @@ void test_Page_readItem__blob_corrupt_data()
uint8_t chunk_start = 0;
uint8_t read_data [32];
// damage blob data
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);
fix.write_raw(128, fix.blob_data, sizeof(fix.blob_data));
// Try to read blob with damaged blob_data
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.readItem(NVSValidPageFixture::NS_INDEX,
ItemType::BLOB_DATA,
"test_blob",
@ -498,8 +509,33 @@ void test_Page_readItem__blob_corrupt_data()
32,
chunk_start));
TEST_ASSERT_EQUAL(3, fix.page.getUsedEntryCount());
TEST_ASSERT_EQUAL(1, fix.page.getErasedEntryCount());
/*
This test cannot be implemented same way as it was with CMock
The fix.page.readItem will read the data using esp_partition_read from offsets in sequence
96, 128, 96. Original implementation has provided different data to the read from offset 96, see fragment of original code below
...
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);
...
Then the result was in deleting only one entry and subsequent getUsedEntryCount has
returned 3 and getErasedEntryCount has returned 1
Current implementation provides damaged data on index 128 and always provides same data on index 96
It leads to the outlined expected erasing of blob entry as well as data of blob entry and thus
getUsedEntryCount returns 2 and getErasedEntryCount returns 2
*/
uint8_t raw_result [4] = {0x82, 0xff, 0xff, 0xff};
TEST_ASSERT_EQUAL(ESP_OK, fix.compare_raw(32, raw_result, sizeof(raw_result)));
TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount());
TEST_ASSERT_EQUAL(2, fix.page.getErasedEntryCount());
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state());
}
@ -515,11 +551,6 @@ void test_Page_readItem__blob_read_entry_suceeds()
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",
@ -547,8 +578,7 @@ void test_Page_cmp__item_not_found()
{
NVSValidPageFixture fix;
// no expectations here since comparison uses the item hash list
// comparison uses the item hash list
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.cmpItem(uint8_t(1), "different", 47));
}
@ -557,9 +587,6 @@ 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)));
}
@ -568,9 +595,6 @@ 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)));
}
@ -579,60 +603,45 @@ 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 chunk_start = 0;
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};
NVSValidBlobPageFixture fix;
TEST_ASSERT_EQUAL(ESP_ERR_NVS_CONTENT_DIFFERS,
fix.page.cmpItem(uint8_t(1),
ItemType::BLOB_DATA,
"test_blob",
blob_data_different,
32));
32,
chunk_start));
}
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 chunk_start = 0;
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};
NVSValidBlobPageFixture fix;
TEST_ASSERT_EQUAL(ESP_OK,
fix.page.cmpItem(NVSValidPageFixture::NS_INDEX,
ItemType::BLOB_DATA,
"test_blob",
blob_data_same,
32));
32,
chunk_start));
}
void test_Page_eraseItem__uninitialized()
@ -666,19 +675,11 @@ void test_Page_eraseItem__write_fail()
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);
// it is expected, that nvs will try to erasing entry by setting bit in entry table (0xfa -> 0xf2)
// simulated write failure should fail the eraseItem call
fix.fail_write_at(1);
// 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(ESP_ERR_FLASH_OP_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());
@ -691,26 +692,10 @@ 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());
}
@ -721,7 +706,7 @@ void test_Page_findItem__uninitialized()
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));
page.findItem(NVSValidPageFixture::NS_INDEX, nvs::ItemType::U8, "test_value", index, item));
}
void test_Page_find__wrong_ns()
@ -740,10 +725,6 @@ void test_Page_find__wrong_type()
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));
}
@ -797,25 +778,11 @@ void test_Page_markFull__wrong_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;
@ -827,14 +794,22 @@ void test_Page_markFreeing__wrong_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_markFull__write_fail()
{
NVSValidPageFixture fix;
// simulate failure during the page state update propagation to the flash
fix.fail_write_at(1);
TEST_ASSERT_EQUAL(ESP_ERR_FLASH_OP_FAIL, fix.page.markFreeing());
TEST_ASSERT_EQUAL(Page::PageState::INVALID, fix.page.state());
}
void test_Page_getVarDataTailroom__uninitialized_page()
{
NVSPageFixture fix;
@ -866,17 +841,17 @@ void test_Page_calcEntries__uninit()
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);
PartitionEmulationFixture fix;
Page page;
// Prepare corrupted header at index 0
fix.write_raw(0, raw_header_corrupt, sizeof(raw_header_corrupt));
// 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(ESP_OK, page.load(&fix.part, 0));
TEST_ASSERT_EQUAL(Page::PageState::CORRUPT, page.state());
@ -933,9 +908,12 @@ void test_Page_calcEntries__invalid()
int main(int argc, char **argv)
{
#define TEMPORARILY_DISABLED(x)
UNITY_BEGIN();
RUN_TEST(test_Page_load_reading_header_fails);
RUN_TEST(test_Page_load_reading_data_fails);
// TEMPORARILY_DISABLED function error codes - to be decided, how to emulate failing esp_partition layer
TEMPORARILY_DISABLED(RUN_TEST(test_Page_load_reading_header_fails);)
TEMPORARILY_DISABLED(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__corrupt_entry_table);
@ -948,13 +926,13 @@ int main(int argc, char **argv)
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);
TEMPORARILY_DISABLED(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);
TEMPORARILY_DISABLED(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);
TEMPORARILY_DISABLED(RUN_TEST(test_Page_readItem__blob_read_data_fails);)
TEMPORARILY_DISABLED(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);
@ -966,7 +944,7 @@ int main(int argc, char **argv)
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);
TEMPORARILY_DISABLED(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);
@ -976,8 +954,8 @@ int main(int argc, char **argv)
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__wrong_state);
RUN_TEST(test_Page_markFreeing__success);
RUN_TEST(test_Page_markFull__write_fail);
RUN_TEST(test_Page_getVarDataTailroom__uninitialized_page);

View File

@ -0,0 +1,310 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "nvs_partition.hpp"
#include "esp_private/partition_linux.h"
#include "nvs.h"
#include "nvs_page.hpp"
#include "nvs_storage.hpp"
#include "nvs_partition.hpp"
#include <exception>
#include <string>
#include "esp_log.h"
struct FixtureException : std::exception {
FixtureException(const std::string& msg) : msg(msg) { }
const char *what() const noexcept {
return msg.c_str();
}
std::string msg;
};
class PartitionEmulationFixture {
public:
PartitionEmulationFixture( uint32_t start_sector = 0,
uint32_t sector_count = 1,
const char *partition_name = NVS_DEFAULT_PART_NAME) :
esp_partition(),
part(&esp_partition)
{
if (esp_partition_file_mmap((const uint8_t **) &p_part_desc_addr_start) != ESP_OK) {
TEST_FAIL_MESSAGE("Failed to initialize esp_partition_file_mmap");
}
esp_partition.address = start_sector * SPI_FLASH_SEC_SIZE;
esp_partition.size = (start_sector + sector_count) * SPI_FLASH_SEC_SIZE;
esp_partition.erase_size = ESP_PARTITION_EMULATED_SECTOR_SIZE;
esp_partition.type = ESP_PARTITION_TYPE_DATA;
esp_partition.subtype = ESP_PARTITION_SUBTYPE_DATA_NVS;
strncpy(esp_partition.label, partition_name, PART_NAME_MAX_SIZE);
}
~PartitionEmulationFixture()
{
// ensure underlying mmaped file gets deleted after unmap.
esp_partition_file_mmap_ctrl_t *p_ctrl = esp_partition_get_file_mmap_ctrl_input();
p_ctrl->remove_dump = true;
esp_partition_file_munmap();
}
public:
// erases all partition space
esp_err_t erase_all()
{
return esp_partition_erase_range(&esp_partition, esp_partition.address, esp_partition.size);
}
// ensures that n-th attempt to esp_partition_write or esp_partition_write_raw fails. To fail 1st attemtpt specify 1, 2nd 2...to disable, call with 0
void fail_write_at(const size_t failing_attempt_no)
{
esp_partition_clear_stats();
size_t err_delay = (failing_attempt_no == 0) ? SIZE_MAX : failing_attempt_no - 1;
esp_partition_fail_after(err_delay, ESP_PARTITION_FAIL_AFTER_MODE_WRITE);
}
// ensures that n-th attempt to esp_partition_erase_range fails. To fail 1st attemtpt specify 1, 2nd 2...to disable, call with 0
void fail_erase_at(const size_t failing_attempt_no)
{
esp_partition_clear_stats();
size_t err_delay = (failing_attempt_no == 0) ? SIZE_MAX : failing_attempt_no - 1;
esp_partition_fail_after(err_delay, ESP_PARTITION_FAIL_AFTER_MODE_ERASE);
}
// writes block of data to the offset relative to the beginning of partition
esp_err_t write_raw(const size_t dst_offset, const void* src, size_t size)
{
// instead of esp_partition_write_raw we will write directly to the mapped memory as some of our write
// operations are actually simulating wrong flash behaviour and linux emulator prevents usto do invalid flash operations
// mapped memory base: p_part_desc_addr_start
// partition begin offset: esp_partition.address
// partition size esp_partition.size
if((dst_offset + size) > esp_partition.size) return ESP_ERR_INVALID_SIZE;
memcpy(p_part_desc_addr_start + esp_partition.address + dst_offset, src, size);
return ESP_OK;
}
// dumps content of memory at given dst_offset and of the size to the console
esp_err_t dumpRaw(const size_t dst_offset, size_t size)
{
const size_t column_size = 4; //number of bytes in column
const size_t column_count = 8; //number of columns on the line
uint8_t buff[column_size]; // buffer for data from flash, one column
if(size == 0) return ESP_ERR_NO_MEM;
if(size % column_size) return ESP_ERR_INVALID_SIZE; // must be multiple of 4
esp_err_t err = ESP_OK;
size_t columns = size / column_size;
volatile size_t column;
for(column = 0; column < columns; column = column + 1)
{
// read column
if((err = esp_partition_read_raw(&esp_partition, dst_offset + (column * column_size), buff, column_size)) != ESP_OK) return err;
// print column
printf("%02x%02x%02x%02x",
((uint8_t*) buff)[0],
((uint8_t*) buff)[1],
((uint8_t*) buff)[2],
((uint8_t*) buff)[3]);
// if it is last column of line or last column at all, print newline, else print space
if ( ((column+1) % column_count) == 0 || ((column+1) == columns) )
{
printf("\n");
}
else
{
printf(" ");
}
}
return ESP_OK;
}
// compares partition content at dst_offset with data block at address src of length size
// returns ESP_OK if data matches
// returns ESP_ERR_NOT_FOUND id data doesn't match
// returns any other error code in case partition read operation fails
esp_err_t compare_raw(const size_t dst_offset, const void* src, size_t size)
{
if(size == 0) return ESP_ERR_NO_MEM;
if(src == nullptr) return ESP_ERR_INVALID_ARG;
uint8_t* buff = ( uint8_t*) malloc(size);
if(buff == nullptr) return ESP_ERR_NO_MEM;
esp_err_t err = ESP_OK;
do {
if((err = esp_partition_read_raw(&esp_partition, dst_offset, buff, size)) != ESP_OK) break;
if(memcmp(buff, src, size) != 0) err = ESP_ERR_NOT_FOUND;
} while (false);
if(err!=ESP_OK) {
ESP_LOGI(TAG, "::compare_raw Data do not match. Beginning of data from flash:");
// print up to first 32 bytes read from flash
static const size_t max_chars = 32;
for(size_t i = 0; i < ((max_chars < size) ? max_chars : size); i++) {
ESP_LOGI(TAG, "0x%02x", ((uint8_t*) buff)[i]);
}
}
free(buff);
return err;
}
protected:
esp_partition_t esp_partition;
public:
nvs::NVSPartition part;
private:
uint8_t *p_part_desc_addr_start;
};
// Prepares empty (filled with 0xff) space in NVS partition and initializes nvs::Page with pointer to this data.
struct NVSPageFixture : public PartitionEmulationFixture {
nvs::Page page;
NVSPageFixture(uint32_t start_sector = 0,
uint32_t sector_count = 1,
const char *partition_name = NVS_DEFAULT_PART_NAME)
: PartitionEmulationFixture(start_sector, sector_count, partition_name), page()
{
erase_all();
if (page.load(&part, start_sector) != ESP_OK) throw FixtureException("couldn't setup page");
}
};
// Prepares page fixture with one namespace, and one value entry
struct NVSValidPageFixture : public PartitionEmulationFixture {
const static uint8_t NS_INDEX = 1;
// raw header
uint8_t raw_header [4];
// valid header
uint8_t raw_header_valid [32];
// entry table with one entry
uint8_t raw_entry_table [32];
// namespace entry
uint8_t ns_entry [32];
// value entry
uint8_t value_entry [32];
nvs::Page page;
NVSValidPageFixture(uint32_t start_sector = 0,
uint32_t sector_size = 1,
const char *partition_name = NVS_DEFAULT_PART_NAME)
: PartitionEmulationFixture(start_sector, sector_size, partition_name),
raw_header {0xff, 0xff, 0xff, 0xff},
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;
// Expected sequence of data blocks read by page.load
write_raw( 0, raw_header_valid, sizeof(raw_header_valid));
write_raw( 32, raw_entry_table, sizeof(raw_entry_table));
write_raw(128, raw_header, sizeof(raw_header));
write_raw( 64, ns_entry, sizeof(ns_entry));
write_raw( 96, value_entry, sizeof(value_entry));
if (page.load(&part, start_sector) != ESP_OK) throw FixtureException("couldn't setup page");
}
};
// Prepares page fixture with one namespace, and one blob value entry
struct NVSValidBlobPageFixture : public PartitionEmulationFixture {
const static uint8_t NS_INDEX = 1;
const static size_t BLOB_DATA_SIZE = 32;
// raw header
uint8_t raw_header [512];
// valid header
uint8_t raw_header_valid [32];
// entry table with one entry
uint8_t raw_entry_table [32];
// namespace entry
uint8_t ns_entry [32];
// blob entry
uint8_t blob_entry [32];
// blob data space
uint8_t blob_data [BLOB_DATA_SIZE];
// blob index entry
uint8_t blob_index [32];
nvs::Page page;
NVSValidBlobPageFixture(uint32_t start_sector = 0,
uint32_t sector_size = 1,
const char *partition_name = NVS_DEFAULT_PART_NAME)
: PartitionEmulationFixture(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()
{
erase_all();
std::fill_n(raw_header, sizeof(raw_header)/sizeof(raw_header[0]), 0xFF);
std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0xFF);
raw_entry_table[0] = 0xaa;
// Expected sequence of data blocks read by page.load
// read page header 32B
write_raw( 0, raw_header_valid, sizeof(raw_header_valid));
// read entry table 32B
write_raw( 32, raw_entry_table, sizeof(raw_entry_table));
// read next free entry's header 512B
write_raw(192, raw_header, sizeof(raw_header));
// read namespace entry 32B
write_raw( 64, ns_entry, sizeof(ns_entry));
// read normal blob entry + index, not the data 32B
write_raw( 96, blob_entry, sizeof(blob_entry));
// not read but prepare also blob data 32B
write_raw(128, blob_data, sizeof(blob_data));
// will be read twice, second time during duplicated entry check 32B
write_raw(160, blob_index, sizeof(blob_index));
if (page.load(&part, start_sector) != ESP_OK) throw FixtureException("couldn't setup page");
}
};

View File

@ -3,3 +3,4 @@ CONFIG_COMPILER_HIDE_PATHS_MACROS=n
CONFIG_IDF_TARGET="linux"
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_NVS_ASSERT_ERROR_CHECK=y
CONFIG_ESP_PARTITION_ENABLE_STATS=y