From 5f8e991ed8cc4725a3b64ec958bdf0a0885d4e66 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 25 Apr 2026 04:44:35 -0500 Subject: [PATCH] [api] Populate schema defaults for transitive cpp test deps; add json override - script/build_helpers.py: when injecting a non-MULTI_CONF component into the post-validation config, run its CONFIG_SCHEMA with {} so defaults are populated. Without this, socket got config = {} and socket.FILTER_SOURCE_FILES crashed with KeyError on 'implementation' (the schema's defaulted key was never filled in). Falls back to {} if the schema can't validate empty input. - tests/components/json/__init__.py: enable codegen for json so its to_code runs during cpp unit test builds, registering the ArduinoJson library. Required for any api dep test, since json_util.cpp #includes . Locally verified 'script/cpp_unit_test.py api' now compiles and runs; ProtoMacVarint test suite (9 cases) passes. --- script/build_helpers.py | 20 +++++++++++++++----- tests/components/json/__init__.py | 9 +++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 tests/components/json/__init__.py diff --git a/script/build_helpers.py b/script/build_helpers.py index 59dded66dd..4cf2f93fbb 100644 --- a/script/build_helpers.py +++ b/script/build_helpers.py @@ -326,11 +326,21 @@ def compile_and_get_binary( # system, not a real loadable component (get_component returns None) elif (component := get_component(component_name)) is not None: # MULTI_CONF components store their config as a list of dicts, - # everything else stores a single dict. Using the wrong shape - # breaks code paths that subscript CORE.config[component] with - # a string key (e.g. socket.FILTER_SOURCE_FILES). - default = [] if component.multi_conf else {} - config.setdefault(component_name, default) + # everything else stores a single dict. Run the component's + # schema with {} so defaults get populated -- code paths like + # socket.FILTER_SOURCE_FILES expect a fully-populated mapping. + if component.multi_conf: + config.setdefault(component_name, []) + elif component_name not in config: + schema = component.config_schema + try: + config[component_name] = schema({}) if schema is not None else {} + except Exception: # noqa: BLE001 + # Schema requires explicit input we can't synthesize; fall + # back to an empty mapping so subscripting at least returns + # KeyError on missing keys rather than crashing on the + # wrong type. + config[component_name] = {} # Register platforms from the extra config (benchmark.yaml) so # USE_SENSOR, USE_LIGHT, etc. defines are emitted without needing diff --git a/tests/components/json/__init__.py b/tests/components/json/__init__.py new file mode 100644 index 0000000000..40ec1f996e --- /dev/null +++ b/tests/components/json/__init__.py @@ -0,0 +1,9 @@ +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + # json's to_code calls cg.add_library("bblanchon/ArduinoJson", ...). C++ + # unit test builds that pull json in transitively (e.g. api) need that + # library registration to happen, otherwise json_util.cpp fails to find + # ArduinoJson.h. + manifest.enable_codegen()