feat(storage): Added Block Device Layer support for esp_partition component

This commit is contained in:
Martin Vychodil
2025-06-26 12:46:40 +02:00
committed by BOT
parent 46def9972a
commit 4493b39d18
20 changed files with 640 additions and 46 deletions

View File

@@ -0,0 +1,7 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/esp_partition/test_apps:
enable:
- if: IDF_TARGET in ["esp32", "esp32c3"]
temporary: true
reason: the other targets are not tested yet

View File

@@ -0,0 +1,8 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp_partition_test)

View File

@@ -0,0 +1,18 @@
| Supported Targets | ESP32 | ESP32-C3 |
| ----------------- | ----- | -------- |
This is a test app for esp_partition component. It verifies all important APIs and properties, and prints the results.
The Block Device Layer related tests have names with a prefix 'test_bdl', and they check all the BDL operations related to 'esp_partition' including parallel access to 2 partitions.
In CI, it is sufficient to run this test for one chip of each architecture.
# Building and Running
To run locally:
```bash
idf.py build flash monitor
```
The tests will be executed and the summary will be printed.
Note, when the Python test script is executed in internal CI, it will test each configuration one by one. When executing this script locally, it will use whichever binary is already built and available in `build` directory.

View File

@@ -0,0 +1,5 @@
idf_component_register(SRCS test_part_app.c
PRIV_INCLUDE_DIRS .
PRIV_REQUIRES unity esp_partition spi_flash
WHOLE_ARCHIVE
)

View File

@@ -0,0 +1,157 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <inttypes.h>
#include "unity.h"
#include "unity_fixture.h"
#include "esp_flash.h"
#include "esp_partition.h"
#include "sdkconfig.h"
TEST_GROUP(esp_partition);
TEST_SETUP(esp_partition)
{
}
TEST_TEAR_DOWN(esp_partition)
{
}
TEST(esp_partition, test_bdl_interface)
{
//get the block-device interface instance
esp_blockdev_handle_t part_blockdev = NULL;
TEST_ESP_OK(esp_partition_get_blockdev(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1", &part_blockdev));
const size_t data_size = 256;
uint8_t test_data[data_size];
const off_t target_addr = 3*4*1024;
uint8_t data_buffer[data_size];
memset((void*)data_buffer, 0, data_size);
//erase the first sector data from the blockdev and check it's really wiped
TEST_ESP_OK(part_blockdev->erase(part_blockdev, target_addr, part_blockdev->geometry.erase_size));
memset((void*)test_data, 0xFF, data_size); //erased NOR flash sector contains only 1s
TEST_ESP_OK(part_blockdev->read(part_blockdev, data_buffer, sizeof(data_buffer), target_addr, data_size));
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer, data_size));
//write to the blockdev
memset((void*)test_data, 'A', data_size);
TEST_ESP_OK(part_blockdev->write(part_blockdev, test_data, target_addr, data_size));
//read from the blockdev the data written before
TEST_ESP_OK(part_blockdev->read(part_blockdev, data_buffer, sizeof(data_buffer), target_addr, data_size));
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer, data_size));
//release the BDL object - nothing to check here, the BDL memory is just freed
esp_partition_release_blockdev(part_blockdev);
}
TEST(esp_partition, test_bdl_interface_external)
{
//register external partition
const esp_partition_t* ext_partition;
TEST_ESP_OK(esp_partition_register_external(NULL, 0x1D0000, 0x20000, "storage3", ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, &ext_partition));
//get the block-device interface instance and test all the ops over external partition
esp_blockdev_handle_t part_blockdev = NULL;
TEST_ESP_OK(esp_partition_ptr_get_blockdev(ext_partition, &part_blockdev));
const size_t data_size = 256;
uint8_t test_data[data_size];
const off_t target_addr = 3*4*1024;
uint8_t data_buffer[data_size];
memset((void*)data_buffer, 0, data_size);
//erase the first sector data from the blockdev and check it's really wiped
TEST_ESP_OK(part_blockdev->erase(part_blockdev, target_addr, part_blockdev->geometry.erase_size));
memset((void*)test_data, 0xFF, data_size); //erased NOR flash sector contains only 1s
TEST_ESP_OK(part_blockdev->read(part_blockdev, data_buffer, sizeof(data_buffer), target_addr, data_size));
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer, data_size));
//write to the blockdev
memset((void*)test_data, 'A', data_size);
TEST_ESP_OK(part_blockdev->write(part_blockdev, test_data, target_addr, data_size));
//read from the blockdev the data written before
TEST_ESP_OK(part_blockdev->read(part_blockdev, data_buffer, sizeof(data_buffer), target_addr, data_size));
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer, data_size));
//release the BDL object - nothing to check here, the BDL memory is just freed
esp_partition_release_blockdev(part_blockdev);
//deregister the external partition
TEST_ESP_OK(esp_partition_deregister_external(ext_partition));
}
TEST(esp_partition, test_bdl_two_partitions)
{
//get the block-device interface instance for partition 'storage1'
esp_blockdev_handle_t part_blockdev_1 = NULL;
TEST_ESP_OK(esp_partition_get_blockdev(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1", &part_blockdev_1));
//get pointer to esp_partition_t object for partition 'storage2'
esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage2");
TEST_ASSERT_NOT_NULL(iter);
const esp_partition_t *part = esp_partition_get(iter);
TEST_ASSERT_NOT_NULL(part);
esp_partition_iterator_release(iter);
esp_blockdev_handle_t part_blockdev_2 = NULL;
TEST_ESP_OK(esp_partition_ptr_get_blockdev(part, &part_blockdev_2));
//erase & write & read data on both partitions in parallel
const size_t data_size = 256;
uint8_t test_data[data_size];
const off_t target_addr = 3*4*1024;
uint8_t data_buffer_1[data_size];
uint8_t data_buffer_2[data_size];
memset((void*)data_buffer_1, 0, data_size);
memset((void*)data_buffer_2, 0, data_size);
//erase the first sector data from the blockdev and check it's really wiped
TEST_ESP_OK(part_blockdev_1->erase(part_blockdev_1, target_addr, part_blockdev_1->geometry.erase_size));
TEST_ESP_OK(part_blockdev_2->erase(part_blockdev_2, target_addr, part_blockdev_2->geometry.erase_size));
memset((void*)test_data, 0xFF, data_size); //erased NOR flash sector contains only 1s
TEST_ESP_OK(part_blockdev_1->read(part_blockdev_1, data_buffer_1, sizeof(data_buffer_1), target_addr, data_size));
TEST_ESP_OK(part_blockdev_2->read(part_blockdev_2, data_buffer_2, sizeof(data_buffer_2), target_addr, data_size));
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer_1, data_size));
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer_2, data_size));
//write to the blockdev 1
memset((void*)test_data, 'A', data_size);
TEST_ESP_OK(part_blockdev_1->write(part_blockdev_1, test_data, target_addr, data_size));
//read the data written before from the blockdev 1
TEST_ESP_OK(part_blockdev_1->read(part_blockdev_1, data_buffer_1, sizeof(data_buffer_1), target_addr, data_size));
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer_1, data_size));
//write to the blockdev 2
memset((void*)test_data, 'B', data_size);
TEST_ESP_OK(part_blockdev_2->write(part_blockdev_2, test_data, target_addr, data_size));
//read the data written before from the blockdev 2
TEST_ESP_OK(part_blockdev_2->read(part_blockdev_2, data_buffer_2, sizeof(data_buffer_2), target_addr, data_size));
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer_2, data_size));
//release the BDL object - nothing to check here, the BDL memory is just freed
esp_partition_release_blockdev(part_blockdev_1);
esp_partition_release_blockdev(part_blockdev_2);
}
TEST_GROUP_RUNNER(esp_partition)
{
RUN_TEST_CASE(esp_partition, test_bdl_interface)
RUN_TEST_CASE(esp_partition, test_bdl_interface_external)
RUN_TEST_CASE(esp_partition, test_bdl_two_partitions)
}
void app_main(void)
{
UNITY_MAIN(esp_partition);
}

View File

@@ -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, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage1, data, , 0x110000, 512K
storage2, data, , 0x190000, 256K,
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, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage1, data, , 0x110000, 512K
7 storage2, data, , 0x190000, 256K,

View File

@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.generic
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
def test_esp_partition(dut: Dut) -> None:
dut.expect_unity_test_output()

View File

@@ -0,0 +1,7 @@
# unity framework
CONFIG_UNITY_ENABLE_FIXTURE=y
# use custom partition table
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"