| [`ExecutorNode.__init__()`](blockly_executor/executor_node.py:27) | Creates the Action Server on topic `execute_blockly_action`. Reads ROS2 parameter `use_real_hardware` (bool, default `False`) to select Hardware mode (dummy or real). |
**Important design decision:** The execute callback always calls `goal_handle.succeed()` regardless of whether the command succeeded or failed. The `result.success` and `result.message` fields communicate command-level outcome. Using `goal_handle.abort()` causes result delivery failures with `rmw_fastrtps_cpp`.
**Purpose:** Maps command names to handler functions using `@handler` decorator and auto-discovery. Mirrors the JS frontend's `BlockRegistry.register()` pattern.
**Auto-discovery:** On `HandlerRegistry.__init__`, all `.py` files in `handlers/` are imported automatically. The `@handler` decorator collects `(command, function)` pairs, and the registry binds `hardware` to each function. No manual imports or module lists needed.
Unified `Hardware` class that provides mode-aware context to all handlers. Handlers check `hardware.is_real()` to decide between dummy (logging) and real (ROS2 publish) behavior.
In real mode, handlers create ROS2 publishers/subscribers lazily on `hardware.node` to communicate with hardware nodes (e.g. `gpio_node`) running on the Raspberry Pi.
The single ROS2 action interface used for all commands. See [Section 2.3](../../docs/architecture.md#23-ros2-interface-contract) for the full definition.
Built by `pixi run build-interfaces` using colcon. The generated Python module is importable as:
```python
from blockly_interfaces.action import BlocklyAction
```
---
### 6.4 Test Suite
Tests are located at [`src/blockly_executor/test/`](test/conftest.py).
#### [`test/conftest.py`](test/conftest.py) — Shared Test Fixtures
See [Section 9.2](#92-conftestpy--shared-fixtures) for detailed explanation.
Tests for `digital_out` and `digital_in` commands: happy path (HIGH/LOW), feedback verification, missing parameter failure, and numeric return value for digital_in.
| `setup-ui` | Downloads Blockly via npm and copies to `src/blockly_app/blockly_app/ui/vendor/` | — |
---
---
## 9. Testing
### 9.1 Testing Philosophy
All tests are **integration tests** that communicate through the real ROS2 Action interface — not unit tests that call internal functions directly. This provides high confidence because the test exercises the exact same communication path as the real application.
**Key architectural decisions:**
- **Executor runs as a separate process** — eliminates race conditions from two threads competing for rclpy's global context