fix(gateway): reconstruct nested plugin config from dotted ROS params#518
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes plugin configuration extraction in ros2_medkit_gateway so ROS parameters under plugins.<name>.* correctly reconstruct nested JSON objects from dotted keys (instead of producing a flat JSON map that plugins never match), and adds unit tests to lock in the expected nested shape.
Changes:
- Move
extract_plugin_config()(and its helpers) intoparam_utils.hppand rebuild nested JSON config by splitting dotted keys into object hierarchies. - Remove the old flat extraction implementation from
gateway_node.cppand use the shared helper. - Extend
test_plugin_configwith cases covering 2-/3-level nesting, array leaves,.pathexclusion, and leaf/intermediate collisions.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/ros2_medkit_gateway/test/test_plugin_config.cpp | Adds unit coverage asserting nested JSON reconstruction and collision behavior for plugin config extraction. |
| src/ros2_medkit_gateway/src/gateway_node.cpp | Removes the old flat extract_plugin_config() implementation and uses the shared helper from param_utils.hpp. |
| src/ros2_medkit_gateway/include/ros2_medkit_gateway/param_utils.hpp | Implements nested insertion for dotted keys and exposes extract_plugin_config() for reuse/testing. |
bburda
left a comment
There was a problem hiding this comment.
Test coverage gaps (outside the diff):
-
Nested reconstruction is only tested through the NodeOptions parameter_overrides source. Both
extract_plugin_configtests set overrides, soconfigis non-empty and the function returns before the--params-filepath runs (declare_plugin_params_from_yaml->list_parameters-> the secondinsert_nested_paramloop). That YAML path is the production one. The existing YAML tests calldeclare_plugin_params_from_yamldirectly and only check flat single-level keys; none callextract_plugin_config. So a regression in the second source (thelist_prefixwithout trailing dot, thesubstr, the second loop) would produce flat or empty config in production and no test would fail. Worth adding a test that loads nested keys from a--params-file, builds a node with noparameter_overrides, callsextract_plugin_config, and asserts the nested object (with one array leaf). -
The leaf/intermediate collision test only covers one order: scalar first (
discovery), then the nested key (discovery.enabled). That path hits the intermediate branch atparam_utils.hpp:96-108. The reverse order (nested key first, then the scalar) goes through the leaf guard atparam_utils.hpp:113-119, which no test exercises, even though the docstring says the result is the same in either order. Worth adding the reversed case.
|
Addressed the review-body test-coverage gaps in 3e1c35f:
Built ros2_medkit_gateway and ran ctest -R test_plugin_config in a clean ros:jazzy container: 7/7 pass. |
3e1c35f to
af0baaf
Compare
extract_plugin_config built a flat JSON object keyed by the dotted
parameter remainder, so plugins.<name>.native_alarms.enabled became
{"native_alarms.enabled": true}. Plugins read nested objects
(config["native_alarms"]["enabled"]), so every nested key on the
plugins.<name>.* param surface was silently ignored across all plugins.
Split the dotted remainder into nested objects; single-level keys,
array leaves, both param sources and the .path exclusion are preserved.
An ASSERT_* abort before the trailing rclcpp::shutdown() left rclcpp initialized and corrupted later tests in the process. Introduce a ScopedRclcpp RAII guard that shuts down during stack unwinding and apply it to all five tests.
Add extract_plugin_config coverage for the --params-file source (source 2) so a regression in the production YAML path cannot pass silently, and a reversed leaf/intermediate collision that exercises the leaf guard.
af0baaf to
285c733
Compare
Summary
extract_plugin_config()built a flat JSON object keyed by the dotted parameter remainder. For a ROS parameterplugins.<plugin>.native_alarms.enabledit produced{"native_alarms.enabled": true}. Every plugin, however, reads its config as a nested object (config.contains("native_alarms"),config["native_alarms"]["enabled"]), so the lookup always missed.Effect: the entire
plugins.<name>.*ROS-param surface was silently dead for any nested key, across every plugin - native_alarms + discovery, discovery/auto_browse/auto_alarms, severity_bands, and so on. Only single-level keys, node_map YAML, and flat env vars worked; configuring a plugin's nested blocks through gateway parameters had no effect and raised no error.Fix
Reconstruct a nested JSON object from the dotted keys: after stripping the
plugins.<plugin>.prefix, split the remainder on.and build nested objects. Preserved end to end:parameter_overridesand the--params-fileYAML vialist_parameters),.pathkey stays excluded.A key used both as a leaf and an intermediate (e.g.
discovery: trueanddiscovery.enabled: true) resolves to the nested object deterministically and warns, never crashing.The extraction helpers moved into
param_utils.hppso the behavior is unit-testable.Testing
test_plugin_configgains coverage locking the nested shape: 2-level, 3-level (severity_bands), an array leaf,.pathexclusion, and a leaf/intermediate collision. These assertions fail against the old flat implementation. Built and ran the gateway gtests in aros:jazzycontainer:test_plugin_configpasses (1/1, all cases). clang-format-18 clean.