[esp32] refactor esp32-vfs default configs to FINAL co-routine & add some defaults (#17337)

Co-authored-by: Oliver Kleinecke <kleinecke.oliver@googlemail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: storage split <ai@local>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
Oliver Kleinecke
2026-08-07 12:36:59 -05:00
committed by GitHub
co-authored by Oliver Kleinecke Claude Sonnet 4.6 Copilot Autofix powered by AI storage split pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
parent c0e70d9beb
commit b326cefea7
2 changed files with 225 additions and 52 deletions
+159
View File
@@ -10,11 +10,16 @@ from typing import Any
import pytest
from esphome.components.esp32 import (
KEY_FATFS_REQUIRED,
KEY_VFS_DIR_REQUIRED,
KEY_VFS_SELECT_REQUIRED,
KEY_VFS_TERMIOS_REQUIRED,
VARIANT_ESP32,
VARIANTS,
NetworkSdkconfigData,
_ota_downgrade_protection_errors,
_reconcile_network_sdkconfig,
_reconcile_vfs_fatfs_sdkconfig,
)
from esphome.components.esp32.const import (
KEY_ESP32,
@@ -614,6 +619,160 @@ def test_reconcile_network_sdkconfig(
assert CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] == expected
@pytest.mark.parametrize(
("requires", "fatfs_required", "disables", "preset", "expected"),
[
# Nothing required and every disable_* flag off (NOT the shipped defaults, which
# disable everything): VFS enabled, FATFS left untouched entirely.
pytest.param(
{},
False,
(False, False, False, False),
{},
{
"CONFIG_VFS_SUPPORT_TERMIOS": True,
"CONFIG_VFS_SUPPORT_SELECT": True,
"CONFIG_VFS_SUPPORT_DIR": True,
},
id="nothing_disabled_nothing_required",
),
# The shipped out-of-the-box path: every disable_* flag defaults to True and nothing
# is required -- VFS off, FATFS at the smallest footprint (8.3 names, one volume).
pytest.param(
{},
False,
(True, True, True, True),
{},
{
"CONFIG_VFS_SUPPORT_TERMIOS": False,
"CONFIG_VFS_SUPPORT_SELECT": False,
"CONFIG_VFS_SUPPORT_DIR": False,
"CONFIG_FATFS_LFN_NONE": True,
"CONFIG_FATFS_VOLUME_COUNT": 1,
},
id="all_disabled_fatfs_fallback",
),
# A component's require_* beats the user's disable_* flag for every VFS feature.
pytest.param(
{
KEY_VFS_TERMIOS_REQUIRED: True,
KEY_VFS_SELECT_REQUIRED: True,
KEY_VFS_DIR_REQUIRED: True,
},
False,
(True, True, True, False),
{},
{
"CONFIG_VFS_SUPPORT_TERMIOS": True,
"CONFIG_VFS_SUPPORT_SELECT": True,
"CONFIG_VFS_SUPPORT_DIR": True,
},
id="require_beats_disable",
),
# A user sdkconfig_options preset wins over a require (the set_opt guard).
pytest.param(
{KEY_VFS_SELECT_REQUIRED: True},
False,
(False, False, False, False),
{"CONFIG_VFS_SUPPORT_SELECT": False},
{
"CONFIG_VFS_SUPPORT_TERMIOS": True,
"CONFIG_VFS_SUPPORT_SELECT": False,
"CONFIG_VFS_SUPPORT_DIR": True,
},
id="user_preset_wins_over_require",
),
# require_fatfs() with no user preset: long filenames on the heap, 255 chars,
# four volumes.
pytest.param(
{},
True,
(False, False, False, False),
{},
{
"CONFIG_VFS_SUPPORT_TERMIOS": True,
"CONFIG_VFS_SUPPORT_SELECT": True,
"CONFIG_VFS_SUPPORT_DIR": True,
"CONFIG_FATFS_LFN_NONE": False,
"CONFIG_FATFS_LFN_HEAP": True,
"CONFIG_FATFS_MAX_LFN": 255,
"CONFIG_FATFS_VOLUME_COUNT": 4,
},
id="fatfs_required_defaults",
),
# CONFIG_FATFS_LONG_FILENAMES is a Kconfig choice: a user picking any member
# (here LFN_STACK) leaves the whole group untouched -- no second =y in the choice.
pytest.param(
{},
True,
(False, False, False, False),
{"CONFIG_FATFS_LFN_STACK": "y"},
{
"CONFIG_VFS_SUPPORT_TERMIOS": True,
"CONFIG_VFS_SUPPORT_SELECT": True,
"CONFIG_VFS_SUPPORT_DIR": True,
"CONFIG_FATFS_LFN_STACK": "y",
"CONFIG_FATFS_VOLUME_COUNT": 4,
},
id="fatfs_user_lfn_stack_untouched",
),
# disable_fatfs (the shipped default) with a user LFN pick: the choice group is the
# user's -- no LFN_NONE=y written next to their member, only the volume fallback.
pytest.param(
{},
False,
(False, False, False, True),
{"CONFIG_FATFS_LFN_HEAP": "y"},
{
"CONFIG_VFS_SUPPORT_TERMIOS": True,
"CONFIG_VFS_SUPPORT_SELECT": True,
"CONFIG_VFS_SUPPORT_DIR": True,
"CONFIG_FATFS_LFN_HEAP": "y",
"CONFIG_FATFS_VOLUME_COUNT": 1,
},
id="disable_fatfs_user_lfn_untouched",
),
# Same for an explicit LFN_NONE preset: the group is the user's, only the volume
# count default is added.
pytest.param(
{},
True,
(False, False, False, False),
{"CONFIG_FATFS_LFN_NONE": "y"},
{
"CONFIG_VFS_SUPPORT_TERMIOS": True,
"CONFIG_VFS_SUPPORT_SELECT": True,
"CONFIG_VFS_SUPPORT_DIR": True,
"CONFIG_FATFS_LFN_NONE": "y",
"CONFIG_FATFS_VOLUME_COUNT": 4,
},
id="fatfs_user_lfn_none_untouched",
),
],
)
def test_reconcile_vfs_fatfs_sdkconfig(
set_core_config: SetCoreConfigCallable,
requires: dict[str, bool],
fatfs_required: bool,
disables: tuple[bool, bool, bool, bool],
preset: dict[str, Any],
expected: dict[str, Any],
) -> None:
"""The FINAL-priority reconciler resolves the VFS feature flags and the FATFS
defaults from the recorded require_* calls, with user sdkconfig_options winning
and the LFN Kconfig choice treated as one group."""
set_core_config(PlatformFramework.ESP32_IDF)
CORE.data[KEY_ESP32] = {KEY_SDKCONFIG_OPTIONS: dict(preset)}
if fatfs_required:
CORE.data[KEY_ESP32][KEY_FATFS_REQUIRED] = True
for key, value in requires.items():
CORE.data[key] = value
asyncio.run(_reconcile_vfs_fatfs_sdkconfig(*disables))
assert CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] == expected
def test_network_wifi_only_reconciles_end_to_end(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],