refactor(nvs_flash): NVS Host tests cleaned-up and test cases commented

- Host test were refactored to allow for BDL and non-BDL NVS implementation
- Introduceed `NVSPartitionTestHelper` class replacing `PartitionEmulationFixture`
- Refactored all tests to use `NVSPartitionTestHelper` instead of legacy emulation fixture
- Removed legacy `PartitionEmulationFixture` and `PartitionEmulationFixture2` classes
- Removed `TEMPORARILY_DISABLED` macro usage by reducing partition size in applicable tests
- Enhanced test coverage and readability with comments and validation steps for each TC
- Added utility functions for partition stats tracking, file loading, and erase count check
This commit is contained in:
radek.tandler
2025-12-02 15:32:14 +01:00
parent 191ac74dd0
commit bbfe8e7492
20 changed files with 2914 additions and 1337 deletions
+1 -1
View File
@@ -1,8 +1,8 @@
components/nvs_flash/host_test: components/nvs_flash/host_test:
depends_components: depends_components:
- spi_flash
- nvs_flash - nvs_flash
- nvs_sec_provider - nvs_sec_provider
- esp_blockdev
- esp_partition - esp_partition
- esp_blockdev - esp_blockdev
enable: enable:
@@ -1,9 +1,11 @@
idf_component_register(SRCS "test_nvs.cpp" idf_component_register(SRCS "bdl_ramdisk.cpp" "test_nvs.cpp"
"test_partition_manager.cpp" "test_partition_manager.cpp"
"test_nvs_cxx_api.cpp" "test_nvs_cxx_api.cpp"
"test_nvs_handle.cpp" "test_nvs_handle.cpp"
"test_nvs_initialization.cpp" "test_nvs_initialization.cpp"
"test_nvs_storage.cpp" "test_nvs_storage.cpp"
"test_fixtures.cpp"
"bdl_ramdisk.cpp"
INCLUDE_DIRS INCLUDE_DIRS
"../../../src" "../../../src"
"../../../private_include" "../../../private_include"
@@ -0,0 +1,202 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bdl_ramdisk.hpp"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
static esp_err_t ramdisk_dev_read(esp_blockdev_handle_t dev_handle, uint8_t* dst_buf, size_t dst_buf_size, uint64_t src_addr, size_t data_read_len)
{
if (!dev_handle || !dst_buf) {
return ESP_ERR_INVALID_ARG;
}
bdl_ramdisk_ctx_t* ctx = (bdl_ramdisk_ctx_t*)dev_handle->ctx;
if (dst_buf_size < data_read_len) {
return ESP_ERR_INVALID_ARG;
}
if (src_addr + data_read_len > ctx->total_size) {
return ESP_ERR_INVALID_SIZE;
}
// Check alignment for read_size
if (dev_handle->geometry.read_size > 0) {
if (src_addr % dev_handle->geometry.read_size != 0 ||
data_read_len % dev_handle->geometry.read_size != 0) {
return ESP_ERR_INVALID_SIZE;
}
}
memcpy(dst_buf, ctx->data + src_addr, data_read_len);
return ESP_OK;
}
static esp_err_t ramdisk_dev_write(esp_blockdev_handle_t dev_handle, const uint8_t* src_buf, uint64_t dst_addr, size_t data_write_len)
{
if (!dev_handle || !src_buf) {
return ESP_ERR_INVALID_ARG;
}
if (dev_handle->device_flags.read_only) {
return ESP_ERR_NOT_SUPPORTED;
}
bdl_ramdisk_ctx_t* ctx = (bdl_ramdisk_ctx_t*)dev_handle->ctx;
if (dst_addr + data_write_len > ctx->total_size) {
return ESP_ERR_INVALID_SIZE;
}
// Check alignment for write_size
if (dev_handle->geometry.write_size > 0) {
if (dst_addr % dev_handle->geometry.write_size != 0 ||
data_write_len % dev_handle->geometry.write_size != 0) {
return ESP_ERR_INVALID_SIZE;
}
}
memcpy(ctx->data + dst_addr, src_buf, data_write_len);
return ESP_OK;
}
static esp_err_t ramdisk_dev_erase(esp_blockdev_handle_t dev_handle, uint64_t start_addr, size_t erase_len)
{
if (!dev_handle) {
return ESP_ERR_INVALID_ARG;
}
if (dev_handle->device_flags.read_only) {
return ESP_ERR_NOT_SUPPORTED;
}
bdl_ramdisk_ctx_t* ctx = (bdl_ramdisk_ctx_t*)dev_handle->ctx;
if (start_addr + erase_len > ctx->total_size) {
return ESP_ERR_INVALID_SIZE;
}
// Check alignment for erase_size
if (dev_handle->geometry.erase_size > 0) {
if (start_addr % dev_handle->geometry.erase_size != 0 ||
erase_len % dev_handle->geometry.erase_size != 0) {
return ESP_ERR_INVALID_SIZE;
}
}
// Fill with default erase value (0xFF for flash-like behavior)
uint8_t erase_value = dev_handle->device_flags.default_val_after_erase ? 0xFF : 0x00;
memset(ctx->data + start_addr, erase_value, erase_len);
return ESP_OK;
}
static esp_err_t ramdisk_dev_sync(esp_blockdev_handle_t dev_handle)
{
// RAM disk doesn't need sync - all operations are immediate
(void)dev_handle;
return ESP_OK;
}
static esp_err_t ramdisk_dev_ioctl(esp_blockdev_handle_t dev_handle, const uint8_t cmd, void* args)
{
if (dev_handle == nullptr || args == nullptr) {
return ESP_ERR_INVALID_ARG;
}
switch (cmd) {
case ESP_BLOCKDEV_CMD_MARK_DELETED:
case ESP_BLOCKDEV_CMD_ERASE_CONTENTS:
// For RAM disk, mark as deleted is equivalent to erase
if (args) {
esp_blockdev_cmd_arg_erase_t* erase_args = (esp_blockdev_cmd_arg_erase_t*)args;
return ramdisk_dev_erase(dev_handle, erase_args->start_addr, erase_args->erase_len);
}
return ESP_ERR_INVALID_ARG;
default:
return ESP_ERR_NOT_SUPPORTED;
}
}
esp_err_t ramdisk_dev_release(esp_blockdev_handle_t dev_handle)
{
if (!dev_handle) {
return ESP_ERR_INVALID_ARG;
}
bdl_ramdisk_ctx_t* ctx = (bdl_ramdisk_ctx_t*)dev_handle->ctx;
if (ctx && ctx->data) {
free(ctx->data);
}
free(ctx);
free(dev_handle);
return ESP_OK;
}
// Const operations structure - shared by all instances
static const esp_blockdev_ops_t s_bdl_ramdisk_ops = {
.read = ramdisk_dev_read,
.write = ramdisk_dev_write,
.erase = ramdisk_dev_erase,
.sync = ramdisk_dev_sync,
.ioctl = ramdisk_dev_ioctl,
.release = ramdisk_dev_release
};
esp_err_t ramdisk_dev_get_blockdev(esp_blockdev_handle_t* blockdev_handle_out, const size_t total_size, const size_t erase_size)
{
if (total_size == 0 || erase_size == 0 || total_size % erase_size != 0) {
return ESP_ERR_INVALID_ARG;
}
// Allocate the block device structure
esp_blockdev_t* bdl = (esp_blockdev_t*)calloc(1, sizeof(esp_blockdev_t));
if (!bdl) {
return ESP_ERR_NO_MEM;
}
// Allocate the context
bdl_ramdisk_ctx_t* ctx = (bdl_ramdisk_ctx_t*)calloc(1, sizeof(bdl_ramdisk_ctx_t));
if (!ctx) {
free(bdl);
return ESP_ERR_NO_MEM;
}
// Allocate the RAM storage
ctx->data = (uint8_t*)malloc(total_size);
if (!ctx->data) {
free(ctx);
free(bdl);
return ESP_ERR_NO_MEM;
}
// Initialize with erased state (0xFF)
memset(ctx->data, 0xFF, total_size);
ctx->total_size = total_size;
ctx->erase_size = erase_size;
// Setup block device structure
bdl->ctx = ctx;
bdl->ops = &s_bdl_ramdisk_ops;
// Configure device flags for flash-like behavior
ESP_BLOCKDEV_FLAGS_INST_CONFIG_DEFAULT(bdl->device_flags);
// Configure geometry
bdl->geometry.disk_size = total_size;
bdl->geometry.read_size = 1; // Can read single bytes
bdl->geometry.write_size = 1; // Can write single bytes
bdl->geometry.erase_size = erase_size; // Erase block size
bdl->geometry.recommended_read_size = 32; // Optimal read size
bdl->geometry.recommended_write_size = 32; // Optimal write size
bdl->geometry.recommended_erase_size = erase_size;
*blockdev_handle_out = bdl;
return ESP_OK;
}
@@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_blockdev.h"
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief RAM disk context structure
*/
typedef struct {
uint8_t *data; /*!< Pointer to RAM storage */
size_t total_size; /*!< Total size of the RAM disk */
size_t erase_size; /*!< Erase block size */
} bdl_ramdisk_ctx_t;
/**
* @brief Create a new RAM disk block device
*
* @param blockdev_handle_out Pointer to store the created block device handle
* @param total_size Total size of the RAM disk in bytes
* @param erase_size Erase block size in bytes
* @return esp_err_t ESP_OK on success, or an error code on failure
*/
esp_err_t ramdisk_dev_get_blockdev(esp_blockdev_handle_t* blockdev_handle_out, const size_t total_size, const size_t erase_size);
/**
* @brief Release a RAM disk block device
*
* @param handle Handle to the RAM disk to release
* @return esp_err_t ESP_OK on success
*/
esp_err_t ramdisk_dev_release(esp_blockdev_handle_t handle);
#ifdef __cplusplus
}
/**
* @brief C++ wrapper class for RAM disk
*/
class bdl_ramdisk {
public:
bdl_ramdisk(const size_t total_size, const size_t erase_size) {
handle = nullptr;
ramdisk_dev_get_blockdev(&handle, total_size, erase_size);
}
~bdl_ramdisk() {
if (handle) {
ramdisk_dev_release(handle);
}
}
operator esp_blockdev_handle_t() const {
return handle;
}
esp_blockdev_handle_t get_handle() const {
return handle;
}
private:
esp_blockdev_handle_t handle;
};
#endif
@@ -0,0 +1,584 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h" // for CONFIG_NVS_BDL_STACK
#include <catch2/catch_test_macros.hpp> // for Catch2 test macros
#include "test_fixtures.hpp" // for test fixtures class definition
#include "nvs_partition.hpp" // for NVSPartition class
#include "esp_partition.h" // for BDL and native esp_partition related functions
#include "esp_private/partition_linux.h" // for partition_linux functions like statistics and emulated sector size
#include <random> // for std::random_device, std::mt19937, std::generate_n
#include <fcntl.h> // for open()
#include <unistd.h> // for close(), read(), write(), lseek()
#include "nvs_constants.h" // for NVS_CONST_PAGE_SIZE
#include "esp_log.h" // for ESP_LOGE
#define TAG "NVSPartitionTestHelper"
// Initialize the static variable(s) of NVSPartitionTestHelper
int NVSPartitionTestHelper::s_instance_count = 0;
#ifndef CONFIG_NVS_BDL_STACK
NVSPartitionTestHelper::NVSPartitionTestHelper(const char *part_name, const bool erase_partition) :
// First call to the esp_partition_find_first maps the partition memory
nvs::NVSPartition(esp_partition_find_first(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name))
{
// Increase the instance count
s_instance_count++;
CHECK(mESPPartition != nullptr);
// Remember the erase size of the BDL
mOriginalEraseSize = ((esp_partition_t*) mESPPartition)->erase_size;
if(erase_partition)
{
// erase the partition before testing
CHECK(erase_range(0, get_size()) == ESP_OK);
}
}
NVSPartitionTestHelper::~NVSPartitionTestHelper()
{
// Decrease the instance count
s_instance_count--;
// reset the erase size to the original value
set_erase_size(mOriginalEraseSize);
}
esp_err_t NVSPartitionTestHelper::load_from_file(const char *file_name)
{
return load_from_file(get_partition_name(), file_name);
}
uint32_t NVSPartitionTestHelper::get_sectors()
{
return get_size() / NVS_CONST_PAGE_SIZE;
}
esp_err_t NVSPartitionTestHelper::set_erase_size(const size_t size)
{
if(mESPPartition == nullptr) {
return ESP_ERR_INVALID_STATE;
}
((esp_partition_t*) mESPPartition)->erase_size = size;
return ESP_OK;
}
size_t NVSPartitionTestHelper::get_first_emulated_sector_index()
{
if (mESPPartition == nullptr) {
return 0;
}
return ((esp_partition_t*) mESPPartition)->address / ESP_PARTITION_EMULATED_SECTOR_SIZE;
}
size_t NVSPartitionTestHelper::get_sector_erase_count(size_t nvs_sector_index)
{
// The esp_partition_get_sector_erase_count function is global across all partitions
// it's sector index is relative to the emulated flash and the unit of the sector is defined by ESP_PARTITION_EMULATED_SECTOR_SIZE.
// nvs_sector_index parameter is the index of the sector in the partition and the unit of the sector is defined by NVS_CONST_PAGE_SIZE.
// Therefore, we need to convert the sector index to the emulated flash sector index.
if (mESPPartition == nullptr || nvs_sector_index >= get_sectors()) {
return 0; // Invalid sector index
}
// To get the correct sector index, we need to calculate the offset of the first sector
size_t first_sector = get_first_emulated_sector_index();
size_t emulated_sector_index = (nvs_sector_index * NVS_CONST_PAGE_SIZE) / ESP_PARTITION_EMULATED_SECTOR_SIZE;
return esp_partition_get_sector_erase_count(emulated_sector_index + first_sector);
}
// Static methods to make testing easier
esp_err_t NVSPartitionTestHelper::randomize_partition(const char *part_name, const uint32_t seed)
{
// Erase the whole partition before randomizing
esp_err_t ret = erase_partition(part_name);
if (ret != ESP_OK) {
return ret;
}
// Find the partition by name
const esp_partition_t *part = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name);
if (part == nullptr) {
return ESP_ERR_NOT_FOUND;
}
std::random_device rd;
std::mt19937 gen(rd());
gen.seed(seed);
uint8_t buf[NVS_CONST_PAGE_SIZE];
// loop over all sectors in the partition
// and fill them with random data
for (uint32_t i = 0; i < part->size / NVS_CONST_PAGE_SIZE; i++)
{
// fill the buffer with random data
std::generate_n(buf, NVS_CONST_PAGE_SIZE, gen);
ret = esp_partition_write(part, i * NVS_CONST_PAGE_SIZE, buf, NVS_CONST_PAGE_SIZE);
if (ret != ESP_OK) {
break;
}
}
return ret;
}
// Erase the whole partition
esp_err_t NVSPartitionTestHelper::erase_partition(const char *part_name)
{
// Find the partition by name
const esp_partition_t *part = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name);
if (part == nullptr) {
return ESP_ERR_NOT_FOUND;
}
// Erase the whole partition
return esp_partition_erase_range(part, 0, part->size);
}
// Get number of NVS sectors in the partition
uint32_t NVSPartitionTestHelper::get_sectors(const char *part_name)
{
// Find the partition by name
const esp_partition_t *part = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name);
if (part == nullptr) {
return 0;
}
return part->size / NVS_CONST_PAGE_SIZE;
}
// Load the partition from a file
esp_err_t NVSPartitionTestHelper::load_from_file(const char *part_name, const char *file_name)
{
// Find the partition by name
const esp_partition_t *part = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name);
if (part == nullptr) {
return ESP_ERR_NOT_FOUND;
}
// Load the partition from file
int file_fd = open(file_name, O_RDONLY);
if (file_fd == -1) {
return ESP_ERR_NOT_FOUND;
}
off_t size = lseek(file_fd, 0L, SEEK_END);
if (size < 0) {
close(file_fd);
return ESP_ERR_INVALID_SIZE;
}
// Check if file fits into the partition
if (size > part->size) {
close(file_fd);
return ESP_ERR_INVALID_SIZE;
}
void *p_buff = malloc((size_t)size);
if (p_buff == nullptr) {
close(file_fd);
return ESP_ERR_NO_MEM;
}
lseek(file_fd, 0L, SEEK_SET);
ssize_t read_size = ::read(file_fd, p_buff, size);
close(file_fd);
if (read_size != size) {
free(p_buff);
return ESP_ERR_INVALID_SIZE;
}
esp_err_t ret = esp_partition_erase_range(part, 0, part->size);
if (ret != ESP_OK) {
free(p_buff);
return ret;
}
ret = esp_partition_write(part, 0, p_buff, size);
free(p_buff);
return ret;
}
void NVSPartitionTestHelper::clear_stats(void)
{
return esp_partition_clear_stats();
}
size_t NVSPartitionTestHelper::get_read_ops(void)
{
return esp_partition_get_read_ops();
}
size_t NVSPartitionTestHelper::get_write_ops(void)
{
return esp_partition_get_write_ops();
}
size_t NVSPartitionTestHelper::get_erase_ops(void)
{
return esp_partition_get_erase_ops();
}
size_t NVSPartitionTestHelper::get_read_bytes(void)
{
return esp_partition_get_read_bytes();
}
size_t NVSPartitionTestHelper::get_write_bytes(void)
{
return esp_partition_get_write_bytes();
}
size_t NVSPartitionTestHelper::get_total_time(void)
{
return esp_partition_get_total_time();
}
void NVSPartitionTestHelper::fail_after(size_t count, uint8_t mode)
{
esp_partition_fail_after(count, mode);
}
#else // CONFIG_NVS_BDL_STACK
// BDL implementation of NVSPartitionTestHelper
NVSPartitionTestHelper::NVSPartitionTestHelper(const char *part_name, const bool erase_partition) :
// Call parent constructor with non-zero though invalid pointer to BDL handle, we will retrieve the correct one later
nvs::NVSPartition(part_name, (esp_blockdev_handle_t)0x1 , false)
{
// Retrieve the BDL handle for the partition
esp_err_t err = esp_partition_get_blockdev(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name,
&mBDL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get BDL handle for NVS partition %s: %s", part_name, esp_err_to_name(err));
std::abort();
}
CHECK(mBDL != nullptr);
// Increase the instance count
s_instance_count++;
// Remember the erase size of the BDL
mOriginalEraseSize = mBDL->geometry.erase_size;
if(erase_partition)
{
// erase the partition before testing
CHECK(erase_range(0, get_size()) == ESP_OK);
}
}
NVSPartitionTestHelper::~NVSPartitionTestHelper()
{
// reset the erase size to the original value
set_erase_size(mOriginalEraseSize);
// Decrease the instance count
s_instance_count--;
}
esp_err_t NVSPartitionTestHelper::load_from_file(const char *file_name)
{
return load_from_file(get_partition_name(), file_name);
}
uint32_t NVSPartitionTestHelper::get_sectors()
{
return get_size() / NVS_CONST_PAGE_SIZE;
}
esp_err_t NVSPartitionTestHelper::set_erase_size(const size_t size)
{
// Set the new erase size in the BDL geometry
mBDL->geometry.erase_size = size;
// We have to set also the erase size in the esp_partition_t structure
const esp_partition_t* part = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
get_partition_name());
if(part != nullptr) {
((esp_partition_t*)part)->erase_size = size;
return ESP_OK;
}
return ESP_ERR_NOT_FOUND;
}
size_t NVSPartitionTestHelper::get_first_emulated_sector_index()
{
// TODO: Change this implementation to reflect the concept of sectors counting for statistics once it is
// implemented in BDL.
// Current implementation assumes the esp_partition API and it's linux implementation evaluates statistics
// based on the emulated flash sectors, which are defined by ESP_PARTITION_EMULATED_SECTOR_SIZE.
// In order to evaluate the statistics correctly, we need to return index of the first sector number this
// partition resides in the emulated flash.
const esp_partition_t* part = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
get_partition_name());
CHECK(part != nullptr);
return part->address / ESP_PARTITION_EMULATED_SECTOR_SIZE;
}
size_t NVSPartitionTestHelper::get_sector_erase_count(size_t nvs_sector_index)
{
// TODO: Once BDL implementation supports sector erase count tracking, we will need to re-implement
// The esp_partition_get_sector_erase_count function is global across all partitions
// it's sector index is relative to the emulated flash and the unit of the sector is defined by ESP_PARTITION_EMULATED_SECTOR_SIZE.
// nvs_sector_index parameter is the index of the sector in the partition and the unit of the sector is defined by NVS_CONST_PAGE_SIZE.
// Therefore, we need to convert the sector index to the emulated flash sector index.
CHECK(nvs_sector_index < get_sectors());
// The statistics are based on the emulated flash sectors, which are defined by ESP_PARTITION_EMULATED_SECTOR_SIZE.
// Therefore, we need to convert the sector index to the emulated flash sector index.
// Get the first sector index of the partition in the emulated flash
size_t first_sector = get_first_emulated_sector_index();
// Calculate the emulated sector index based on the NVS sector index (the unis are defined by NVS_CONST_PAGE_SIZE)
size_t emulated_sector_index = (nvs_sector_index * NVS_CONST_PAGE_SIZE) / ESP_PARTITION_EMULATED_SECTOR_SIZE;
// Call the esp_partition_get_sector_erase_count function with the emulated sector index
return esp_partition_get_sector_erase_count(emulated_sector_index + first_sector);
}
// Static methods to make testing easier
esp_err_t NVSPartitionTestHelper::randomize_partition(const char *part_name, const uint32_t seed)
{
// Erase the whole partition before randomizing
esp_err_t ret = erase_partition(part_name);
if (ret != ESP_OK) {
return ret;
}
esp_blockdev_handle_t bdl_handle;
// get the BDL handle for the partition
ret = esp_partition_get_blockdev(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name,
&bdl_handle);
if (ret != ESP_OK) {
return ret;
}
std::random_device rd;
std::mt19937 gen(rd());
gen.seed(seed);
uint8_t buf[NVS_CONST_PAGE_SIZE];
// loop over all sectors in the partition
// and fill them with random data
for (uint32_t i = 0; i < bdl_handle->geometry.disk_size / NVS_CONST_PAGE_SIZE; i++)
{
// fill the buffer with random data
std::generate_n(buf, NVS_CONST_PAGE_SIZE, gen);
// Write the random data to the partition using the BDL handle
ret = bdl_handle->ops->write(bdl_handle, buf, i * NVS_CONST_PAGE_SIZE, NVS_CONST_PAGE_SIZE);
if (ret != ESP_OK) {
break;
}
}
return ret;
}
// Erase the whole partition
esp_err_t NVSPartitionTestHelper::erase_partition(const char *part_name)
{
// get the BDL handle for the partition
esp_blockdev_handle_t bdl_handle;
esp_err_t ret = esp_partition_get_blockdev(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name,
&bdl_handle);
if (ret != ESP_OK) {
return ret;
}
// Erase the whole partition
return bdl_handle->ops->erase(bdl_handle, 0, bdl_handle->geometry.disk_size);
}
// Get number of NVS sectors in the partition
uint32_t NVSPartitionTestHelper::get_sectors(const char *part_name)
{
// get the BDL handle for the partition
esp_blockdev_handle_t bdl_handle;
esp_err_t ret = esp_partition_get_blockdev(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name,
&bdl_handle);
if (ret != ESP_OK) {
return 0;
}
return bdl_handle->geometry.disk_size / NVS_CONST_PAGE_SIZE;
}
// Load the partition from a file
esp_err_t NVSPartitionTestHelper::load_from_file(const char *part_name, const char *file_name)
{
// get the BDL handle for the partition
esp_blockdev_handle_t bdl_handle;
esp_err_t ret = esp_partition_get_blockdev(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_NVS,
part_name,
&bdl_handle);
if (ret != ESP_OK) {
return ret;
}
// Load the partition from file
int file_fd = open(file_name, O_RDONLY);
if (file_fd == -1) {
return ESP_ERR_NOT_FOUND;
}
off_t size = lseek(file_fd, 0L, SEEK_END);
if (size < 0) {
close(file_fd);
return ESP_ERR_INVALID_SIZE;
}
// Check if file fits into the partition
if (size > bdl_handle->geometry.disk_size) {
close(file_fd);
return ESP_ERR_INVALID_SIZE;
}
void *p_buff = malloc((size_t)size);
if (p_buff == nullptr) {
close(file_fd);
return ESP_ERR_NO_MEM;
}
lseek(file_fd, 0L, SEEK_SET);
ssize_t read_size = ::read(file_fd, p_buff, size);
close(file_fd);
if (read_size != size) {
free(p_buff);
return ESP_ERR_INVALID_SIZE;
}
ret = erase_partition(part_name);
if (ret != ESP_OK) {
free(p_buff);
return ret;
}
ret = bdl_handle->ops->write(bdl_handle, (const uint8_t*) p_buff, 0, size);
free(p_buff);
return ret;
}
void NVSPartitionTestHelper::clear_stats(void)
{
// TODO: Once BDL implementation supports statistics, we will need to re-implement this function
return esp_partition_clear_stats();
}
size_t NVSPartitionTestHelper::get_read_ops(void)
{
// TODO: Once BDL implementation supports statistics, we will need to re-implement this function
return esp_partition_get_read_ops();
}
size_t NVSPartitionTestHelper::get_write_ops(void)
{
// TODO: Once BDL implementation supports statistics, we will need to re-implement this function
return esp_partition_get_write_ops();
}
size_t NVSPartitionTestHelper::get_erase_ops(void)
{
// TODO: Once BDL implementation supports statistics, we will need to re-implement this function
return esp_partition_get_erase_ops();
}
size_t NVSPartitionTestHelper::get_read_bytes(void)
{
// TODO: Once BDL implementation supports statistics, we will need to re-implement this function
return esp_partition_get_read_bytes();
}
size_t NVSPartitionTestHelper::get_write_bytes(void)
{
// TODO: Once BDL implementation supports statistics, we will need to re-implement this function
return esp_partition_get_write_bytes();
}
size_t NVSPartitionTestHelper::get_total_time(void)
{
// TODO: Once BDL implementation supports statistics, we will need to re-implement this function
return esp_partition_get_total_time();
}
void NVSPartitionTestHelper::fail_after(size_t count, uint8_t mode)
{
// TODO: Once BDL implementation supports programmed failures, we will need to re-implement this function
esp_partition_fail_after(count, mode);
}
#endif
@@ -1,206 +1,75 @@
/* /*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include "nvs_partition.hpp"
#include "esp_private/partition_linux.h"
#include "nvs.h"
#include <random>
#include <fcntl.h>
#include <unistd.h>
#include "esp_partition.h"
class PartitionEmulationFixture { #pragma once
#include "nvs_partition.hpp" // for NVSPartition class
#include "esp_err.h" // for esp_err_t
#include "esp_private/partition_linux.h" // for constants of the fail_after() ESP_PARTITION_FAIL_AFTER_MODE_BOTH
class NVSPartitionTestHelper : public nvs::NVSPartition {
public: public:
PartitionEmulationFixture( uint32_t start_sector = 0, // Constructor uses esp_partition API to initialize the partition
uint32_t sector_size = 1, // Parameters:
const char *partition_name = NVS_DEFAULT_PART_NAME) : // - part_name: name of the partition to be used for testing
esp_partition() // - erase_partition: if true, the partition will be erased before testing
{ NVSPartitionTestHelper(const char *part_name, const bool erase_partition = true);
if (esp_partition_file_mmap((const uint8_t **) &p_part_desc_addr_start) != ESP_OK) { // Destructor decreases the instance count
FAIL("Failed to initialize esp_partition_file_mmap"); // and unmaps the partition memory if this is the last instance
} virtual ~NVSPartitionTestHelper();
const uint32_t sec_size = esp_partition_get_main_flash_sector_size(); // Load the partition from a file
esp_partition.address = start_sector * sec_size; esp_err_t load_from_file(const char *file_name);
esp_partition.size = (start_sector + sector_size) * 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, NVS_PART_NAME_MAX_SIZE);
p_part = new (std::nothrow) nvs::NVSPartition(&esp_partition);
REQUIRE(p_part != nullptr);
}
// initializes the partition and loads partition binary file into it // Return the number of NVS sectors in the partition
PartitionEmulationFixture( uint32_t start_sector, uint32_t get_sectors();
uint32_t sector_size,
const char *partition_name,
const char *partition_binary) : PartitionEmulationFixture(start_sector, sector_size, partition_name)
{
int file_fd = -1;
off_t size = -1;
void *p_buff = nullptr;
char const *fail_msg = nullptr;
const uint32_t sec_size = esp_partition_get_main_flash_sector_size();
do { // Overrides the erase size to the specified size
// get file size esp_err_t set_erase_size(const size_t size);
file_fd = open(partition_binary, O_RDONLY);
if (file_fd == -1) {
fail_msg = "Failed to open file with partition content";
break;
}
size = lseek(file_fd, 0L, SEEK_END);
if (size < 0) {
fail_msg = "failed to seek in file with partition content";
break;
}
// check if file fits into the partitiion // Function returns the starting sector index of the partition in the emulated flash
if (size > sector_size * sec_size) { // The size of emulated sector is defined by ESP_PARTITION_EMULATED_SECTOR_SIZE
fail_msg = "file with partition content doesn't fit into the partition"; // First emulated sector of the partition is any number, it depends on the partition location
break; // in the emulated flash
} size_t get_first_emulated_sector_index();
// allocate local buffer // Get the erase count of a specific NVS sector within the partition
p_buff = malloc((size_t) size); // The size of NVS sector is defined by NVS_CONST_PAGE_SIZE
if (p_buff == nullptr) { // First sector index is 0
fail_msg = "unable to allocate buffer for reading file with partition content"; size_t get_sector_erase_count(size_t nvs_sector_index);
break;
}
// laoad file into local buffer //--------------- Static helper methods to make testing easier --------------------------
int res = lseek(file_fd, 0L, SEEK_SET);
if (res < 0) {
fail_msg = "failed to seek in file with partition content";
break;
}
size = read(file_fd, p_buff, size);
if (size < 0) {
fail_msg = "cannot read file with partition content";
break;
}
// erase whole partition // Randomize the whole partition with a given seed
if (ESP_OK != esp_partition_erase_range(&esp_partition, 0, sector_size * sec_size)) { static esp_err_t randomize_partition(const char *part_name, const uint32_t seed);
fail_msg = "cannot erase partition prior to write partition binary from file";
break;
}
// write local buffer to the partition // Erase the whole partition
if (ESP_OK != esp_partition_write_raw(&esp_partition, 0, p_buff, size)) { static esp_err_t erase_partition(const char *part_name);
fail_msg = "cannot write to the partition";
break;
}
} while (false);
// close file // Get number of NVS sectors in the partition
if (file_fd >= 0) { static uint32_t get_sectors(const char *part_name);
close(file_fd);
}
// deallocate buffer // Load the partition from a file
if (p_buff != nullptr) { static esp_err_t load_from_file(const char *part_name, const char *file_name);
free(p_buff);
}
if(fail_msg != nullptr) { // Set of functions to access partition statistics
FAIL(fail_msg); // At the moment these functions are global, it means that they do not distinguish between
} // different partitions
} // Be aware that host tests mixing multiple partitions
// will not work correctly, as the statistics will be summed up for all partitions
void randomize(uint32_t seed) static void clear_stats(void);
{ static size_t get_read_ops(void);
std::random_device rd; static size_t get_write_ops(void);
std::mt19937 gen(rd()); static size_t get_erase_ops(void);
gen.seed(seed); static size_t get_read_bytes(void);
static size_t get_write_bytes(void);
esp_partition_file_mmap_ctrl_t *p_ctrl = esp_partition_get_file_mmap_ctrl_act(); static size_t get_total_time(void);
REQUIRE(p_ctrl != nullptr); static void fail_after(size_t count, uint8_t mode);
std::generate_n(p_part_desc_addr_start, p_ctrl->flash_file_size, gen); private:
} static int s_instance_count; // Static variable to track instance count
size_t mOriginalEraseSize; // Erase size of the Partition at the moment of the object creation
// absolute sectorNumber is used here
bool erase(size_t sectorNumber)
{
const uint32_t sec_size = esp_partition_get_main_flash_sector_size();
size_t offset = sectorNumber * sec_size;
// check the upper bound
esp_partition_file_mmap_ctrl_t *p_ctrl = esp_partition_get_file_mmap_ctrl_act();
REQUIRE(p_ctrl != nullptr);
if (offset > p_ctrl->flash_file_size) {
return false;
}
// esp_partition_erase_range uses offset relative to the beginning of partition
return (esp_partition_erase_range(&esp_partition,
offset - esp_partition.address,
sec_size) == ESP_OK);
}
~PartitionEmulationFixture()
{
delete p_part;
// ensure underlying mapped 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();
}
nvs::NVSPartition *part()
{
return p_part;
}
const esp_partition_t *get_esp_partition() const
{
return &esp_partition;
}
nvs::NVSPartition *p_part;
esp_partition_t esp_partition;
uint8_t *p_part_desc_addr_start;
};
// fixture with 2 partitions
class PartitionEmulationFixture2 : public PartitionEmulationFixture {
public:
PartitionEmulationFixture2( uint32_t start_sector1 = 0,
uint32_t sector_size1 = 1,
const char *partition_name1 = "nvs1",
uint32_t start_sector2 = 1,
uint32_t sector_size2 = 1,
const char *partition_name2 = "nvs2"
) :
PartitionEmulationFixture(start_sector1, sector_size1, partition_name1), esp_partition2()
{
const uint32_t sec_size = esp_partition_get_main_flash_sector_size();
// for 2nd partition
esp_partition2.address = start_sector2 * sec_size;
esp_partition2.size = (start_sector2 + sector_size2) * sec_size;
esp_partition2.erase_size = ESP_PARTITION_EMULATED_SECTOR_SIZE;
esp_partition2.type = ESP_PARTITION_TYPE_DATA;
esp_partition2.subtype = ESP_PARTITION_SUBTYPE_DATA_NVS;
strncpy(esp_partition2.label, partition_name2, NVS_PART_NAME_MAX_SIZE);
p_part2 = new (std::nothrow) nvs::NVSPartition(&esp_partition2);
REQUIRE(p_part2 != nullptr);
}
~PartitionEmulationFixture2()
{
delete p_part2;
}
nvs::NVSPartition *part2()
{
return p_part2;
}
nvs::NVSPartition *p_part2;
esp_partition_t esp_partition2;
}; };
File diff suppressed because it is too large Load Diff
@@ -1,49 +1,51 @@
/* /*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <algorithm> #include "nvs_flash.h"
#include <cstring>
#include "nvs_handle_simple.hpp" #include "nvs_handle_simple.hpp"
#include "nvs_partition_manager.hpp" #include "nvs_partition_manager.hpp"
#include "test_fixtures.hpp"
#include <iostream> #include <string>
#include <vector>
using namespace std; using namespace std;
#define TEST_DEFAULT_PARTITION_NAME "nvs"
TEST_CASE("NVSHandleSimple CXX api open invalid arguments", "[nvs cxx]") TEST_CASE("NVSHandleSimple CXX api open invalid arguments", "[nvs cxx]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Test case for invalid arguments in open_nvs_handle_from_partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; // Combination of nullptr partition name and namespace name
PartitionEmulationFixture f(0, 10, "test");
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()-> // Init the default partition
init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
handle = nvs::open_nvs_handle_from_partition(nullptr, "ns_1", NVS_READWRITE, &result); handle = nvs::open_nvs_handle_from_partition(nullptr, "ns_1", NVS_READWRITE, &result);
CHECK(result == ESP_ERR_INVALID_ARG); CHECK(result == ESP_ERR_INVALID_ARG);
CHECK(!handle); CHECK(!handle);
handle = nvs::open_nvs_handle_from_partition("test", nullptr, NVS_READWRITE, &result); handle = nvs::open_nvs_handle_from_partition(TEST_DEFAULT_PARTITION_NAME, nullptr, NVS_READWRITE, &result);
CHECK(result == ESP_ERR_INVALID_ARG); CHECK(result == ESP_ERR_INVALID_ARG);
CHECK(!handle); CHECK(!handle);
nvs::NVSPartitionManager::get_instance()->deinit_partition("test"); nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME);
} }
TEST_CASE("NVSHandleSimple CXX api open partition uninitialized", "[nvs cxx]") TEST_CASE("NVSHandleSimple CXX api open partition uninitialized", "[nvs cxx]")
{ {
uint8_t *p_part_desc_addr_start; // Negative TC for opening a handle from a partition that has not been initialized
CHECK(esp_partition_file_mmap((const uint8_t **)&p_part_desc_addr_start) == ESP_OK);
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> handle;
handle = nvs::open_nvs_handle_from_partition("test", "ns_1", NVS_READWRITE, &result); handle = nvs::open_nvs_handle_from_partition(TEST_DEFAULT_PARTITION_NAME, "ns_1", NVS_READWRITE, &result);
bool result_expected = result == ESP_ERR_NVS_NOT_INITIALIZED || result == ESP_ERR_NVS_PART_NOT_FOUND; bool result_expected = result == ESP_ERR_NVS_NOT_INITIALIZED || result == ESP_ERR_NVS_PART_NOT_FOUND;
CHECK(result_expected); CHECK(result_expected);
CHECK(!handle); CHECK(!handle);
@@ -51,18 +53,20 @@ TEST_CASE("NVSHandleSimple CXX api open partition uninitialized", "[nvs cxx]")
TEST_CASE("NVSHandleSimple CXX api open successful", "[nvs cxx]") TEST_CASE("NVSHandleSimple CXX api open successful", "[nvs cxx]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for opening a handle using open_nvs_handle_from_partition from an initialized partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
PartitionEmulationFixture f(0, 10, "test");
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
// Init the default partition
REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
handle = nvs::open_nvs_handle_from_partition("test", "ns_1", NVS_READWRITE, &result); handle = nvs::open_nvs_handle_from_partition(TEST_DEFAULT_PARTITION_NAME, "ns_1", NVS_READWRITE, &result);
CHECK(result == ESP_OK); CHECK(result == ESP_OK);
CHECK(handle); CHECK(handle);
@@ -72,23 +76,27 @@ TEST_CASE("NVSHandleSimple CXX api open successful", "[nvs cxx]")
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
nvs::NVSPartitionManager::get_instance()->deinit_partition("test"); nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME);
} }
TEST_CASE("NVSHandleSimple CXX api open default part successful", "[nvs cxx]") TEST_CASE("NVSHandleSimple CXX api open default part successful", "[nvs cxx]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for opening a handle using open_nvs_handle from an initialized default partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
PartitionEmulationFixture f(0, 10);
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
// Init the default partition
REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
handle = nvs::open_nvs_handle("ns_1", NVS_READWRITE, &result); // Open handle without specifying partition name
// This should open the default partition "nvs"
handle = nvs::open_nvs_handle_from_partition(TEST_DEFAULT_PARTITION_NAME, "ns_1", NVS_READWRITE, &result);
CHECK(result == ESP_OK); CHECK(result == ESP_OK);
CHECK(handle); CHECK(handle);
@@ -98,46 +106,50 @@ TEST_CASE("NVSHandleSimple CXX api open default part successful", "[nvs cxx]")
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
nvs::NVSPartitionManager::get_instance()->deinit_partition("nvs"); nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME);
} }
TEST_CASE("NVSHandleSimple CXX api open default part ns NULL", "[nvs cxx]") TEST_CASE("NVSHandleSimple CXX api open default part ns NULL", "[nvs cxx]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Negative TC for opening a handle using open_nvs_handle from a partition name specified as nullptr
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
PartitionEmulationFixture f(0, 10);
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
// Init the default partition
REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
handle = nvs::open_nvs_handle(nullptr, NVS_READWRITE, &result); handle = nvs::open_nvs_handle_from_partition(TEST_DEFAULT_PARTITION_NAME, nullptr, NVS_READWRITE, &result);
CHECK(result == ESP_ERR_INVALID_ARG); CHECK(result == ESP_ERR_INVALID_ARG);
CHECK(!handle); CHECK(!handle);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
nvs::NVSPartitionManager::get_instance()->deinit_partition("nvs"); nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME);
} }
TEST_CASE("NVSHandleSimple CXX api read/write string", "[nvs cxx]") TEST_CASE("NVSHandleSimple CXX api read/write string", "[nvs cxx]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for reading and writing a string using NVSHandleSimple CXX API
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
PartitionEmulationFixture f(0, 10);
char read_buffer [256]; char read_buffer [256];
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
// Init the default partition
REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
handle = nvs::open_nvs_handle("test_ns", NVS_READWRITE, &result); handle = nvs::open_nvs_handle_from_partition(TEST_DEFAULT_PARTITION_NAME, "test_ns", NVS_READWRITE, &result);
CHECK(result == ESP_OK); CHECK(result == ESP_OK);
REQUIRE(handle); REQUIRE(handle);
@@ -149,25 +161,27 @@ TEST_CASE("NVSHandleSimple CXX api read/write string", "[nvs cxx]")
CHECK(string(read_buffer) == "test string"); CHECK(string(read_buffer) == "test string");
nvs::NVSPartitionManager::get_instance()->deinit_partition("nvs"); nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME);
} }
TEST_CASE("NVSHandleSimple CXX api read/write blob", "[nvs cxx]") TEST_CASE("NVSHandleSimple CXX api read/write blob", "[nvs cxx]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for reading and writing a blob using NVSHandleSimple CXX API
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
PartitionEmulationFixture f(0, 10);
const char blob [6] = {15, 16, 17, 18, 19}; const char blob [6] = {15, 16, 17, 18, 19};
char read_blob[6] = {0}; char read_blob[6] = {0};
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
// Init the default partition
REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
handle = nvs::open_nvs_handle("test_ns", NVS_READWRITE, &result); handle = nvs::open_nvs_handle_from_partition(TEST_DEFAULT_PARTITION_NAME, "test_ns", NVS_READWRITE, &result);
CHECK(result == ESP_OK); CHECK(result == ESP_OK);
REQUIRE(handle); REQUIRE(handle);
@@ -179,5 +193,5 @@ TEST_CASE("NVSHandleSimple CXX api read/write blob", "[nvs cxx]")
CHECK(vector<char>(blob, blob + sizeof(blob)) == vector<char>(read_blob, read_blob + sizeof(read_blob))); CHECK(vector<char>(blob, blob + sizeof(blob)) == vector<char>(read_blob, read_blob + sizeof(read_blob)));
nvs::NVSPartitionManager::get_instance()->deinit_partition("nvs"); nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME);
} }
@@ -1,34 +1,34 @@
/* /*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <algorithm> #include "nvs_flash.h"
#include <cstring>
#include "nvs_handle_simple.hpp" #include "nvs_handle_simple.hpp"
#include "nvs_partition_manager.hpp" #include "nvs_partition_manager.hpp"
#include "test_fixtures.hpp"
#include <iostream>
#include <string>
using namespace std; using namespace std;
#define TEMPORARILY_DISABLED(x) #define TEST_DEFAULT_PARTITION_NAME "nvs" // Default partition name used in the tests - 10 sectors
#define TEST_3SEC_PARTITION_NAME "nvs_3sec" // Partition used in the space constrained tests - 3 sectors
TEST_CASE("NVSHandleSimple closes its reference in PartitionManager", "[partition_mgr]") TEST_CASE("NVSHandleSimple closes its reference in PartitionManager", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC verifying that NVSHandleSimple closes its reference in PartitionManager
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; // when it is deleted.
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
// Init the default partition
REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
nvs::NVSHandleSimple *handle; nvs::NVSHandleSimple *handle = nullptr;
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle) == ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_DEFAULT_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1);
@@ -36,29 +36,29 @@ TEST_CASE("NVSHandleSimple closes its reference in PartitionManager", "[partitio
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[partition_mgr]") TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for multiple open and close of NVSHandleSimple with PartitionManager
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
// Init the default partition
REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
nvs::NVSHandleSimple *handle1; nvs::NVSHandleSimple *handle1 = nullptr;
nvs::NVSHandleSimple *handle2; nvs::NVSHandleSimple *handle2 = nullptr;
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle1) == ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_DEFAULT_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle1) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1);
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle2) == ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_DEFAULT_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle2) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 2); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 2);
@@ -70,61 +70,60 @@ TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[pa
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("NVSHandleSimple readonly fails", "[partition_mgr]") TEST_CASE("NVSHandleSimple readonly fails", "[partition_mgr]")
{ {
PartitionEmulationFixture f(0, 10); // Negative TC for opening a handle with read-only mode and trying to write to it
nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME); // Erase the partition to ensure it is clean
nvs::NVSHandleSimple *handle_1; REQUIRE(nvs_flash_erase_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
nvs::NVSHandleSimple *handle_2;
const uint32_t NVS_FLASH_SECTOR = 6; // Init the default partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; REQUIRE(nvs_flash_init_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);)
nvs::NVSHandleSimple *handle_1 = nullptr;
nvs::NVSHandleSimple *handle_2 = nullptr;
CHECK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
// first, creating namespace... // first, creating namespace...
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle_1) == ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_3SEC_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle_1) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1);
delete handle_1; delete handle_1;
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READONLY, &handle_2) == ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_3SEC_PARTITION_NAME, "ns_1", NVS_READONLY, &handle_2) == ESP_OK);
CHECK(handle_2->set_item("key", 47) == ESP_ERR_NVS_READ_ONLY); CHECK(handle_2->set_item("key", 47) == ESP_ERR_NVS_READ_ONLY);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1);
delete handle_2; delete handle_2;
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
// without deinit it affects "nvs api tests" // without deinit it affects "nvs api tests"
CHECK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("NVSHandleSimple set/get char", "[partition_mgr]") TEST_CASE("NVSHandleSimple set/get char", "[partition_mgr]")
{ {
// Positive TC for setting and getting a char using NVSHandleSimple
enum class TestEnum : char { enum class TestEnum : char {
FOO = -1, FOO = -1,
BEER, BEER,
BAR BAR
}; };
PartitionEmulationFixture f(0, 10); // Erase the partition to ensure it is clean
REQUIRE(nvs_flash_erase_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
const uint32_t NVS_FLASH_SECTOR = 6; // Init the default partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; REQUIRE(nvs_flash_init_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);)
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) nvs::NVSHandleSimple *handle = nullptr;
== ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_3SEC_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
nvs::NVSHandleSimple *handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
char test_e = 'a'; char test_e = 'a';
char test_e_read = 'z'; char test_e_read = 'z';
@@ -136,27 +135,25 @@ TEST_CASE("NVSHandleSimple set/get char", "[partition_mgr]")
delete handle; delete handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("NVSHandleSimple correctly sets/gets int enum", "[partition_mgr]") TEST_CASE("NVSHandleSimple correctly sets/gets int enum", "[partition_mgr]")
{ {
// Positive TC for setting and getting an int enum using NVSHandleSimple
enum class TestEnum : int { enum class TestEnum : int {
FOO, FOO,
BAR BAR
}; };
PartitionEmulationFixture f(0, 10); // Erase the partition to ensure it is clean
REQUIRE(nvs_flash_erase_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
const uint32_t NVS_FLASH_SECTOR = 6; // Init the default partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; REQUIRE(nvs_flash_init_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);)
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) nvs::NVSHandleSimple *handle = nullptr;
== ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_3SEC_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
nvs::NVSHandleSimple *handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
TestEnum test_e = TestEnum::BAR; TestEnum test_e = TestEnum::BAR;
TestEnum test_e_read = TestEnum::FOO; TestEnum test_e_read = TestEnum::FOO;
@@ -168,28 +165,26 @@ TEST_CASE("NVSHandleSimple correctly sets/gets int enum", "[partition_mgr]")
delete handle; delete handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("NVSHandleSimple correctly sets/gets int enum with negative values", "[partition_mgr]") TEST_CASE("NVSHandleSimple correctly sets/gets int enum with negative values", "[partition_mgr]")
{ {
// Positive TC for setting and getting an int enum with negative values using NVSHandleSimple
enum class TestEnum : int { enum class TestEnum : int {
FOO = -1, FOO = -1,
BEER, BEER,
BAR BAR
}; };
PartitionEmulationFixture f(0, 10); // Erase the partition to ensure it is clean
REQUIRE(nvs_flash_erase_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
const uint32_t NVS_FLASH_SECTOR = 6; // Init the default partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; REQUIRE(nvs_flash_init_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);)
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) nvs::NVSHandleSimple *handle = nullptr;
== ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_3SEC_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
nvs::NVSHandleSimple *handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
TestEnum test_e = TestEnum::FOO; TestEnum test_e = TestEnum::FOO;
TestEnum test_e_read = TestEnum::BEER; TestEnum test_e_read = TestEnum::BEER;
@@ -201,27 +196,25 @@ TEST_CASE("NVSHandleSimple correctly sets/gets int enum with negative values", "
delete handle; delete handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("NVSHandleSimple correctly sets/gets uint8_t enum", "[partition_mgr]") TEST_CASE("NVSHandleSimple correctly sets/gets uint8_t enum", "[partition_mgr]")
{ {
// Positive TC for setting and getting a uint8_t enum using NVSHandleSimple
enum class TestEnum : uint8_t { enum class TestEnum : uint8_t {
FOO, FOO,
BAR BAR
}; };
PartitionEmulationFixture f(0, 10); // Erase the partition to ensure it is clean
REQUIRE(nvs_flash_erase_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
const uint32_t NVS_FLASH_SECTOR = 6; // Init the default partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; REQUIRE(nvs_flash_init_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);)
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) nvs::NVSHandleSimple *handle = nullptr;
== ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_3SEC_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
nvs::NVSHandleSimple *handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
TestEnum test_e = TestEnum::BAR; TestEnum test_e = TestEnum::BAR;
TestEnum test_e_read = TestEnum::FOO; TestEnum test_e_read = TestEnum::FOO;
@@ -233,28 +226,26 @@ TEST_CASE("NVSHandleSimple correctly sets/gets uint8_t enum", "[partition_mgr]")
delete handle; delete handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("NVSHandleSimple correctly sets/gets char enum", "[partition_mgr]") TEST_CASE("NVSHandleSimple correctly sets/gets char enum", "[partition_mgr]")
{ {
// Positive TC for setting and getting a char enum using NVSHandleSimple
enum class TestEnum : char { enum class TestEnum : char {
FOO = -1, FOO = -1,
BEER, BEER,
BAR BAR
}; };
PartitionEmulationFixture f(0, 10); // Erase the partition to ensure it is clean
REQUIRE(nvs_flash_erase_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
const uint32_t NVS_FLASH_SECTOR = 6; // Init the default partition
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; REQUIRE(nvs_flash_init_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);)
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) nvs::NVSHandleSimple *handle = nullptr;
== ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_3SEC_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
nvs::NVSHandleSimple *handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
TestEnum test_e = TestEnum::BAR; TestEnum test_e = TestEnum::BAR;
TestEnum test_e_read = TestEnum::FOO; TestEnum test_e_read = TestEnum::FOO;
@@ -266,5 +257,5 @@ TEST_CASE("NVSHandleSimple correctly sets/gets char enum", "[partition_mgr]")
delete handle; delete handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_3SEC_PARTITION_NAME) == ESP_OK);
} }
@@ -1,42 +1,89 @@
/* /*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include "sdkconfig.h"
#include "sdkconfig.h" // for CONFIG_NVS_BDL_STACK
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include "nvs.hpp" #include "nvs_flash.h"
#include "nvs_partition_manager.hpp" #include "nvs_partition_manager.hpp"
#include "nvs_partition.hpp" #include "nvs_partition.hpp"
#include "test_fixtures.hpp" #include "esp_partition.h" // for esp_partition_find_first
#include <string.h> #include "bdl_ramdisk.hpp" // for bdl_ramdisk
#include "esp_partition.h"
using namespace std; using namespace std;
#define TEST_DEFAULT_PARTITION_NAME "nvs"
#ifndef CONFIG_NVS_BDL_STACK
TEST_CASE("nvs_flash_init_partition_ptr fails due to nullptr arg", "[nvs_custom_part]") TEST_CASE("nvs_flash_init_partition_ptr fails due to nullptr arg", "[nvs_custom_part]")
{ {
uint8_t *p_part_desc_addr_start; // This test checks that nvs_flash_init_partition_ptr fails when passed a nullptr as partition argument.
CHECK(esp_partition_file_mmap((const uint8_t **)&p_part_desc_addr_start) == ESP_OK);
CHECK(nvs_flash_init_partition_ptr(nullptr) == ESP_ERR_INVALID_ARG); CHECK(nvs_flash_init_partition_ptr(nullptr) == ESP_ERR_INVALID_ARG);
} }
TEST_CASE("nvs_flash_init_partition_ptr inits one partition", "[nvs_custom_part]") TEST_CASE("nvs_flash_init_partition_ptr inits one partition", "[nvs_custom_part]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // This test initializes a partition with a specific name and checks if it is registered in the NVSPartitionManager.
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; // The test works around the intnernal partition lookup by handing over a partition structure retrieved from esp_partition_find_first.
const uint32_t sec_size = esp_partition_get_main_flash_sector_size();
uint8_t *p_part_desc_addr_start; // Erase the partition to ensure it is clean
CHECK(esp_partition_file_mmap((const uint8_t **)&p_part_desc_addr_start) == ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
esp_partition_t partition = {}; const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, TEST_DEFAULT_PARTITION_NAME);
strcpy(partition.label, "test"); REQUIRE(partition != nullptr);
partition.address = NVS_FLASH_SECTOR * sec_size;
partition.size = NVS_FLASH_SECTOR_COUNT_MIN * sec_size;
CHECK(nvs_flash_init_partition_ptr(&partition) == ESP_OK); CHECK(nvs_flash_init_partition_ptr(partition) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(TEST_DEFAULT_PARTITION_NAME) != nullptr);
CHECK(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK);
REQUIRE(nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
} }
#else
TEST_CASE("nvs_flash_init_partition_bdl fails due to nullptr arg", "[nvs_custom_part]")
{
// This test checks that nvs_flash_init_partition_bdl fails when nullptr is provided as a block device argument
// or the partition name argument is null.
// Check nullptr block device
CHECK(nvs_flash_init_partition_bdl("NullBDL", nullptr) == ESP_ERR_INVALID_ARG);
// Check nullptr partition name
bdl_ramdisk ramdisk(3 * NVS_CONST_PAGE_SIZE, NVS_CONST_PAGE_SIZE);
CHECK(ramdisk.get_handle() != nullptr);
CHECK(nvs_flash_init_partition_bdl(nullptr, ramdisk.get_handle()) == ESP_ERR_INVALID_ARG);
}
TEST_CASE("nvs_flash_init_partition_bdl fails due to ineligible block device", "[nvs_custom_part]")
{
// This test checks that nvs_flash_init_partition_bdl fails when passed an ineligible block device.
const char* part_name = "BadBDL";
bdl_ramdisk ramdisk(3 * NVS_CONST_PAGE_SIZE, NVS_CONST_PAGE_SIZE); // Ramdisk of 3 NVS pages size
CHECK(ramdisk.get_handle() != nullptr);
// Cripple the ramdisk to make it ineligible (erase size not matching NVS requirements)
ramdisk.get_handle()->geometry.erase_size = NVS_CONST_PAGE_SIZE - 1;
CHECK(nvs_flash_init_partition_bdl(part_name, ramdisk.get_handle()) == ESP_ERR_NOT_SUPPORTED);
}
TEST_CASE("nvs_flash_init_partition_bdl inits externally supplied block device", "[nvs_custom_part]")
{
// This test initializes NVS on the externally supplied block device with a specific name and checks if
// it is registered in the NVSPartitionManager.
// The test works around the intnernal partition lookup by handing over a partition structure retrieved from esp_partition_find_first.
// Erase the partition to ensure it is clean
const char* part_name = "GoodBDL";
bdl_ramdisk ramdisk(3 * NVS_CONST_PAGE_SIZE, NVS_CONST_PAGE_SIZE); // Ramdisk of 3 NVS pages size
CHECK(ramdisk.get_handle() != nullptr);
CHECK(nvs_flash_init_partition_bdl(part_name, ramdisk.get_handle()) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(part_name) != nullptr);
REQUIRE(nvs_flash_deinit_partition(part_name) == ESP_OK);
}
#endif // CONFIG_NVS_BDL_STACK
@@ -1,30 +1,34 @@
/* /*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <cstring> #include "nvs_flash.h"
#include "nvs_storage.hpp" #include "nvs_storage.hpp"
#include "nvs_partition_manager.hpp" #include "nvs_partition_manager.hpp"
#include "test_fixtures.hpp"
#include <iostream>
using namespace std; using namespace std;
#define TEST_DEFAULT_PARTITION_NAME "nvs"
TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs_storage]") TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs_storage]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for Storage iterator recognizing a blob with VerOffset::VER_1_OFFSET
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition before testing
== ESP_OK); REQUIRE(nvs_flash_erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
// Init the default partition
REQUIRE(nvs_flash_init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
uint8_t blob [] = {0x0, 0x1, 0x2, 0x3}; uint8_t blob [] = {0x0, 0x1, 0x2, 0x3};
uint8_t blob_new [] = {0x3, 0x2, 0x1, 0x0}; uint8_t blob_new [] = {0x3, 0x2, 0x1, 0x0};
nvs::Storage *storage = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test"); nvs::Storage *storage = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(TEST_DEFAULT_PARTITION_NAME);
uint8_t ns_index; uint8_t ns_index;
REQUIRE(storage != nullptr);
storage->createOrOpenNamespace("test_ns", true, ns_index); storage->createOrOpenNamespace("test_ns", true, ns_index);
CHECK(storage->writeItem(ns_index, nvs::ItemType::BLOB, "test_blob", blob, sizeof(blob)) == ESP_OK); CHECK(storage->writeItem(ns_index, nvs::ItemType::BLOB, "test_blob", blob, sizeof(blob)) == ESP_OK);
@@ -43,5 +47,5 @@ TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs
CHECK(string(it.entry_info.key) == string("test_blob")); CHECK(string(it.entry_info.key) == string("test_blob"));
CHECK(it.entry_info.type == NVS_TYPE_BLOB); CHECK(it.entry_info.type == NVS_TYPE_BLOB);
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); REQUIRE(nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
} }
@@ -1,70 +1,86 @@
/* /*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <catch2/catch_test_macros.hpp>
#include <algorithm> #include <catch2/catch_test_macros.hpp> // for Catch2 test macros
#include <cstring> #include "nvs_handle_simple.hpp" // for NVSHandleSimple class
#include "nvs_handle_simple.hpp" #include "nvs_partition_manager.hpp" // for NVSPartitionManager class
#include "nvs_partition_manager.hpp" #include "test_fixtures.hpp" // for test fixtures
#include "nvs_test_api.h"
#include "test_fixtures.hpp"
using namespace std; using namespace std;
#define TEST_DEFAULT_PARTITION_NAME "nvs"
#define TEST_SECONDARY_PARTITION_NAME "nvs_sec"
TEST_CASE("Partition manager initializes storage", "[partition_mgr]") TEST_CASE("Partition manager initializes storage", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for partition manager initializing storage
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; // This test initializes a partition with a specific name and checks if it is registered in the NVSPartitionManager.
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); // Erase the partition to ensure it is clean
CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); // We cannot use nvs_flash_erase_partition here because it is actually using the NVSPartitionManager we want to test.
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(f.part()->get_partition_name()) == ESP_OK); REQUIRE(NVSPartitionTestHelper::erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(TEST_DEFAULT_PARTITION_NAME) != nullptr);
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("Partition manager de-initializes storage", "[partition_mgr]") TEST_CASE("Partition manager de-initializes storage", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for partition manager de-initializing storage
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; // This test initializes a partition with a specific name and checks if it is registered in the NVSPartitionManager.
PartitionEmulationFixture f(0, 10, "test"); // After de-initialization, the storage should no longer be found
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); // Erase the partition to ensure it is clean
CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); // We cannot use nvs_flash_erase_partition here because it is actually using the NVSPartitionManager we want to test.
CHECK(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); REQUIRE(NVSPartitionTestHelper::erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name("test") == nullptr);
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(TEST_DEFAULT_PARTITION_NAME) != nullptr);
CHECK(nvs::NVSPartitionManager::get_instance()->deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(TEST_DEFAULT_PARTITION_NAME) == nullptr);
} }
TEST_CASE("Partition manager open fails on null handle", "[partition_mgr]") TEST_CASE("Partition manager open fails on null handle", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Negative TC for partition manager open_handle failing on null handle pointer
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; // This test initializes a partition with a specific name.
PartitionEmulationFixture f(0, 10, "test"); // The handle shouldn't be null, so the open_handle should return ESP_ERR_INVALID_ARG
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); // We cannot use nvs_flash_erase_partition here because it is actually using the NVSPartitionManager we want to test.
REQUIRE(NVSPartitionTestHelper::erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, nullptr) // Init the default partition
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_DEFAULT_PARTITION_NAME, "ns_1", NVS_READWRITE, nullptr)
== ESP_ERR_INVALID_ARG); == ESP_ERR_INVALID_ARG);
nvs::NVSPartitionManager::get_instance()->deinit_partition("test"); REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
} }
TEST_CASE("Partition manager invalidates handle on partition de-init", "[partition_mgr]") TEST_CASE("Partition manager invalidates handle on partition de-init", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; // Positive TC for partition manager invalidating handle on partition de-init
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; // This test initializes a partition with a specific name and checks if it is registered in the NVSPartitionManager.
PartitionEmulationFixture f(0, 10, "test"); // After de-initialization, the handle should be invalidated
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) // Erase the partition to ensure it is clean
== ESP_OK); // We cannot use nvs_flash_erase_partition here because it is actually using the NVSPartitionManager we want to test.
REQUIRE(NVSPartitionTestHelper::erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
nvs::NVSHandleSimple *handle; // Init the default partition
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle) == ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
nvs::NVSHandleSimple *handle = nullptr;
REQUIRE(nvs::NVSPartitionManager::get_instance()->open_handle(TEST_DEFAULT_PARTITION_NAME, "ns_1", NVS_READWRITE, &handle) == ESP_OK);
CHECK(handle->erase_all() == ESP_OK); CHECK(handle->erase_all() == ESP_OK);
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
CHECK(handle->erase_all() == ESP_ERR_NVS_INVALID_HANDLE); CHECK(handle->erase_all() == ESP_ERR_NVS_INVALID_HANDLE);
@@ -73,37 +89,31 @@ TEST_CASE("Partition manager invalidates handle on partition de-init", "[partiti
TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]") TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR_BEGIN1 = 0; // Positive TC for partition manager initializing multiple partitions
const uint32_t NVS_FLASH_SECTOR_SIZE1 = 3; // This test initializes two partitions with specific names and checks if they are registered in the NVSPartitionManager.
const char* NVS_FLASH_PARTITION1 = "test1"; // After de-initialization, both partitions shouldn't be found
const uint32_t NVS_FLASH_SECTOR_BEGIN2 = 3;
const uint32_t NVS_FLASH_SECTOR_SIZE2 = 3;
const char* NVS_FLASH_PARTITION2 = "test2";
PartitionEmulationFixture2 f(NVS_FLASH_SECTOR_BEGIN1, // Erase the partitions to ensure they are clean
NVS_FLASH_SECTOR_SIZE1, // We cannot use nvs_flash_erase_partition here because it is actually using the NVSPartitionManager we want to test.
NVS_FLASH_PARTITION1, REQUIRE(NVSPartitionTestHelper::erase_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
NVS_FLASH_SECTOR_BEGIN2, REQUIRE(NVSPartitionTestHelper::erase_partition(TEST_SECONDARY_PARTITION_NAME) == ESP_OK);
NVS_FLASH_SECTOR_SIZE2,
NVS_FLASH_PARTITION2
);
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), // Init the default partition
NVS_FLASH_SECTOR_BEGIN1, REQUIRE(nvs::NVSPartitionManager::get_instance()->init_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
NVS_FLASH_SECTOR_SIZE1)
== ESP_OK);
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(f.part2(), // Init the alternative partition
NVS_FLASH_SECTOR_BEGIN2, REQUIRE(nvs::NVSPartitionManager::get_instance()->init_partition(TEST_SECONDARY_PARTITION_NAME) == ESP_OK);
NVS_FLASH_SECTOR_SIZE2)
== ESP_OK);
nvs::Storage *storage1 = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(NVS_FLASH_PARTITION1); // Check if both partitions are registered
nvs::Storage *storage1 = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(TEST_DEFAULT_PARTITION_NAME);
REQUIRE(storage1 != nullptr); REQUIRE(storage1 != nullptr);
nvs::Storage *storage2 = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(NVS_FLASH_PARTITION2); nvs::Storage *storage2 = nvs::NVSPartitionManager::get_instance()->lookup_storage_from_name(TEST_SECONDARY_PARTITION_NAME);
REQUIRE(storage2 != nullptr); REQUIRE(storage2 != nullptr);
// Check if the two storages are different
CHECK(storage1 != storage2); CHECK(storage1 != storage2);
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_FLASH_PARTITION1) == ESP_OK);
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(NVS_FLASH_PARTITION2) == ESP_OK); // De-initialize both partitions
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(TEST_DEFAULT_PARTITION_NAME) == ESP_OK);
REQUIRE(nvs::NVSPartitionManager::get_instance()->deinit_partition(TEST_SECONDARY_PARTITION_NAME) == ESP_OK);
} }
@@ -0,0 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, , 0xa000,
nvs_sec, data, nvs, , 0xa000,
nvs_3sec, data, nvs, , 0x3000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, , 0xa000,
4 nvs_sec, data, nvs, , 0xa000,
5 nvs_3sec, data, nvs, , 0x3000,
6 phy_init, data, phy, , 0x1000,
7 factory, app, factory, , 1M,
@@ -11,6 +11,7 @@ from pytest_embedded_idf.utils import idf_parametrize
[ [
'default_set_key', 'default_set_key',
'legacy_set_key', 'legacy_set_key',
'esp_blockdev',
], ],
indirect=True, indirect=True,
) )
@@ -0,0 +1,2 @@
# Configuration enabling internal storage via esp_blockdev instead of direct call to esp_partition nvs_api
CONFIG_NVS_BDL_STACK=y
@@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="linux"
CONFIG_COMPILER_CXX_EXCEPTIONS=y CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
CONFIG_ESP_PARTITION_ENABLE_STATS=y CONFIG_ESP_PARTITION_ENABLE_STATS=y
CONFIG_PARTITION_TABLE_SINGLE_APP=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_nvs_host_test.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions_nvs_host_test.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_OFFSET=0x8000
@@ -43,7 +43,8 @@ public:
esp_partition.erase_size = ESP_PARTITION_EMULATED_SECTOR_SIZE; esp_partition.erase_size = ESP_PARTITION_EMULATED_SECTOR_SIZE;
esp_partition.type = ESP_PARTITION_TYPE_DATA; esp_partition.type = ESP_PARTITION_TYPE_DATA;
esp_partition.subtype = ESP_PARTITION_SUBTYPE_DATA_NVS; esp_partition.subtype = ESP_PARTITION_SUBTYPE_DATA_NVS;
strncpy(esp_partition.label, partition_name, PART_NAME_MAX_SIZE); strncpy(esp_partition.label, partition_name, NVS_PART_NAME_MAX_SIZE);
esp_partition.label[NVS_PART_NAME_MAX_SIZE] = 0; // ensure null termination
} }
~PartitionEmulationFixture() ~PartitionEmulationFixture()
+2 -2
View File
@@ -174,8 +174,8 @@ bool NVSPartition::is_bdl_nvs_compliant(const char* label, const esp_blockdev_ha
is_compliant = false; is_compliant = false;
} }
if(bdl->geometry.erase_size % NVS_CONST_PAGE_SIZE != 0) { if(NVS_CONST_PAGE_SIZE % bdl->geometry.erase_size != 0) {
ESP_LOGV(TAG, "Block device erase size is not a multiple of %d, it is %zu", NVS_CONST_PAGE_SIZE, bdl->geometry.erase_size); ESP_LOGV(TAG, "Block device erase size %zu does not allow erasing pages of size %d.", bdl->geometry.erase_size, NVS_CONST_PAGE_SIZE);
is_compliant = false; is_compliant = false;
} }
@@ -12,6 +12,13 @@ def test_nvs_flash(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='!nvs_encr_hmac', timeout=120) dut.run_all_single_board_cases(group='!nvs_encr_hmac', timeout=120)
@pytest.mark.generic
@pytest.mark.parametrize('config', ['blockdev'], indirect=True)
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
def test_nvs_flash_blockdev(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='!nvs_encr_hmac', timeout=120)
@pytest.mark.nvs_encr_hmac @pytest.mark.nvs_encr_hmac
@pytest.mark.parametrize('config', ['nvs_encr_hmac_esp32c3'], indirect=True) @pytest.mark.parametrize('config', ['nvs_encr_hmac_esp32c3'], indirect=True)
@idf_parametrize('target', ['esp32c3'], indirect=['target']) @idf_parametrize('target', ['esp32c3'], indirect=['target'])
@@ -0,0 +1,2 @@
# Configuration enabling internal storage via esp_blockdev instead of direct call to esp_partition nvs_api
CONFIG_NVS_BDL_STACK=y