From 5cd391f7a2f98ccf4589281363eba63ecc5e0f75 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Mon, 11 May 2026 21:55:40 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20S08.11=20switching=5Ftransport=20BDD=20?= =?UTF-8?q?scenario=20(UDP=E2=86=94TCP)=20on=20QEMU?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire BddTargetSwitchConfig_SetByName as the onSwitch callback in the FreeRTOS BDD target so the runtime `switch tcp` / `switch udp` UART commands flip the SwitchingSender's active inner, matching the Linux and Windows targets. Tag the non-@tls switching_transport scenario @udp @tcp so the bdd-freertos-qemu (@udp or @tcp) admission clause picks it up; the @tls sibling stays excluded until S08.07. Closes #340. Co-Authored-By: Claude Opus 4.7 (1M context) --- Bdd/Targets/FreeRtos/README.md | 26 ++++++----- Bdd/Targets/FreeRtos/main.c | 2 +- Bdd/features/switching_transport.feature | 1 + CLAUDE.md | 3 +- DEVLOG.md | 56 ++++++++++++++++++++++++ ci/docker-compose.bdd.yml | 3 ++ 6 files changed, 79 insertions(+), 12 deletions(-) diff --git a/Bdd/Targets/FreeRtos/README.md b/Bdd/Targets/FreeRtos/README.md index d0585221..f33016ad 100644 --- a/Bdd/Targets/FreeRtos/README.md +++ b/Bdd/Targets/FreeRtos/README.md @@ -6,13 +6,17 @@ QEMU's stdio UART. SolidSyslog runs here with the portable `SolidSyslogCircularBuffer` + `SolidSyslogFreeRtosMutex` drained by a dedicated FreeRTOS Service task, -UdpSender over FreeRTOS-Plus-TCP, the `SolidSyslogFreeRtosStaticResolver` -pinned to the QEMU slirp gateway (`10.0.2.2`), and the -`Bdd/Targets/Common/BddTargetInteractive` runner. Drive identity / -endpoint / PRIVAL fields over the UART command channel with -`set NAME VALUE`, emit N RFC 5424 datagrams with `send N`, exit cleanly -with `quit`. See [`Bdd/README.md`](../../Bdd/README.md) for how Behave -pipes these commands through `qemu-system-arm`'s stdio UART. +a `SolidSyslogSwitchingSender` wrapping a UDP sender (over +`SolidSyslogFreeRtosDatagram`) and a TCP sender (over +`SolidSyslogFreeRtosTcpStream` + `SolidSyslogStreamSender`), the +`SolidSyslogFreeRtosStaticResolver` pinned to the QEMU slirp gateway +(`10.0.2.2`), and the `Bdd/Targets/Common/BddTargetInteractive` runner. +Drive identity / endpoint / PRIVAL fields over the UART command channel +with `set NAME VALUE`, flip the active transport at runtime with +`switch udp` / `switch tcp` (routed through `BddTargetSwitchConfig`), +emit N RFC 5424 messages with `send N`, exit cleanly with `quit`. See +[`Bdd/README.md`](../../Bdd/README.md) for how Behave pipes these +commands through `qemu-system-arm`'s stdio UART. `Common/` carries the shared infrastructure (CMSDK UART driver, newlib syscalls, mps2-an385 linker script, startup) and `cmake/` the @@ -122,9 +126,11 @@ polled `_read` / `_write` via newlib retargeting static IPv4 of `10.0.2.15` on the QEMU slirp network, and starts the scheduler. On link-up the IP-task event hook spawns the interactive task (running `BddTargetInteractive_Run`) and the Service task (draining the -`SolidSyslogCircularBuffer` and pushing through the UDP sender). Each -`send N` line over the UART emits N RFC 5424 datagrams to -`{10.0.2.2, port=g_port}`. +`SolidSyslogCircularBuffer` and pushing through the +`SolidSyslogSwitchingSender`'s active inner — UDP by default, TCP after +`switch tcp`). Each `send N` line over the UART emits N RFC 5424 +messages to `{10.0.2.2, port=g_port}` over whichever transport is +currently selected. The FreeRTOS GCC ARM_CM3 port supplies `vPortSVCHandler`, `xPortPendSVHandler`, and `xPortSysTickHandler`. diff --git a/Bdd/Targets/FreeRtos/main.c b/Bdd/Targets/FreeRtos/main.c index f31e2adb..65bd5766 100644 --- a/Bdd/Targets/FreeRtos/main.c +++ b/Bdd/Targets/FreeRtos/main.c @@ -451,7 +451,7 @@ static void InteractiveTask(void* argument) SolidSyslog_SetErrorHandler(ErrorHandler, NULL); SolidSyslog_Create(&config); - BddTargetInteractive_Run(&g_message, stdin, NULL, OnSet); + BddTargetInteractive_Run(&g_message, stdin, BddTargetSwitchConfig_SetByName, OnSet); SolidSyslog_Destroy(); SolidSyslogOriginSd_Destroy(); diff --git a/Bdd/features/switching_transport.feature b/Bdd/features/switching_transport.feature index 8f640672..5a53a9cd 100644 --- a/Bdd/features/switching_transport.feature +++ b/Bdd/features/switching_transport.feature @@ -4,6 +4,7 @@ Feature: Switch transport at runtime SwitchingSender. The --transport CLI flag sets the initial selector value, and the interactive `switch` command updates it at runtime. + @udp @tcp Scenario: Switch from UDP to TCP mid-run delivers via both Given the syslog oracle is running And the BDD target is running with default transport udp diff --git a/CLAUDE.md b/CLAUDE.md index c64d5d96..1d8b3bff 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -329,8 +329,9 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*` | `SolidSyslogFreeRtosDatagram.h` | System setup code on FreeRTOS targets using FreeRTOS-Plus-TCP for UDP | `SolidSyslogFreeRtosDatagramStorage`, `SOLIDSYSLOG_FREERTOSDATAGRAM_SIZE`, `SolidSyslogFreeRtosDatagram_Create(storage)`, `_Destroy(datagram)` (wraps `FreeRTOS_socket` / `FreeRTOS_sendto` / `FreeRTOS_closesocket`) | | `SolidSyslogStream.h` | Sender implementors using stream transport | `SolidSyslogStream_Open`, `_Send`, `_Read`, `_Close`, `SolidSyslogSsize` | | `SolidSyslogStreamDefinition.h` | Stream implementors (extension point) | `SolidSyslogStream` vtable struct (`Open`, `Send`, `Read`, `Close`) | +| `SolidSyslogFreeRtosTcpStream.h` | System setup code on FreeRTOS targets using FreeRTOS-Plus-TCP for TCP | `SolidSyslogFreeRtosTcpStreamStorage`, `SOLIDSYSLOG_FREERTOSTCPSTREAM_SIZE`, `SolidSyslogFreeRtosTcpStream_Create(storage)`, `_Destroy(stream)` (wraps `FreeRTOS_socket` / `FreeRTOS_connect` / `FreeRTOS_send` / `FreeRTOS_recv` / `FreeRTOS_closesocket`; bounded blocking connect via `SO_SNDTIMEO=200ms`, cleared post-connect so Send/Read are non-blocking) | | `SolidSyslogUdpSender.h` | System setup code using UDP transport | `SolidSyslogUdpSenderConfig` (resolver, datagram, endpoint, endpointVersion), `SolidSyslogUdpSender_Create`, `_Destroy` | -| `SolidSyslogStreamSender.h` | System setup code using octet-framed transport (RFC 6587) over any Stream — `SolidSyslogPosixTcpStream` (POSIX TCP), `SolidSyslogWinsockTcpStream` (Windows TCP), `SolidSyslogTlsStream` (TLS; OpenSSL reference integration), or a caller-supplied Stream backend | `SolidSyslogStreamSenderConfig` (resolver, stream, endpoint, endpointVersion), `SolidSyslogStreamSender_Create`, `_Destroy` | +| `SolidSyslogStreamSender.h` | System setup code using octet-framed transport (RFC 6587) over any Stream — `SolidSyslogPosixTcpStream` (POSIX TCP), `SolidSyslogWinsockTcpStream` (Windows TCP), `SolidSyslogFreeRtosTcpStream` (FreeRTOS-Plus-TCP), `SolidSyslogTlsStream` (TLS; OpenSSL reference integration), or a caller-supplied Stream backend | `SolidSyslogStreamSenderConfig` (resolver, stream, endpoint, endpointVersion), `SolidSyslogStreamSender_Create`, `_Destroy` | | `SolidSyslogSwitchingSender.h` | System setup code composing multiple inner senders | `SolidSyslogSwitchingSenderConfig` (senders, senderCount, selector), `SolidSyslogSwitchingSenderSelector`, `SolidSyslogSwitchingSender_Create`, `_Destroy` | | `SolidSyslogBuffer.h` | Library internals consuming a buffer (Service algorithm) | `SolidSyslogBuffer_Write`, `_Read` | | `SolidSyslogBufferDefinition.h` | Buffer implementors (extension point) | `SolidSyslogBuffer` vtable struct | diff --git a/DEVLOG.md b/DEVLOG.md index ffa69d6e..10eb29b6 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -1,5 +1,61 @@ # Dev Log +## 2026-05-11 — S08.11 switching_transport BDD scenario (UDP↔TCP) on QEMU (#340) + +Third and final slice of S08.06. With the TCP stream (S08.09) and TCP +reconnect verification (S08.10) in place, this slice lights up the +runtime `switch` command in the FreeRTOS BDD target and admits the +non-`@tls` `switching_transport.feature` scenario to the QEMU runner. + +### Decisions + +- **Wire `BddTargetSwitchConfig_SetByName` as the `onSwitch` callback, + not just `set transport`.** S08.09 already routed `set transport + ` through `BddTargetSwitchConfig`, so the harness could + flip the initial transport via `--transport tcp` translated to a + `set transport tcp` UART line. The runtime `switch tcp` command + path is a separate dispatch in `BddTargetInteractive.c` — guarded + on `onSwitch != NULL`, so leaving `onSwitch = NULL` meant `switch + tcp` from the BDD step `the client switches to transport tcp` + silently no-op'd. Passing `BddTargetSwitchConfig_SetByName` lines + the FreeRTOS target up with Linux (`Bdd/Targets/Linux/main.c:286`) + and Windows (`Bdd/Targets/Windows/BddTargetWindows.c:357`), which + already pass it. +- **Tag the non-`@tls` scenario `@udp @tcp` at the scenario level, + not the feature level.** The feature is feature-level `@buffered` + only — invisible to the FreeRTOS `(@udp or @tcp)` admission clause. + The `@tls` sibling must stay excluded until S08.07 lands TLS on + FreeRTOS, so feature-level `@tcp` would over-admit. Scenario-level + `@udp @tcp` on the first scenario keeps the second one out without + restructuring the docker-compose filter. +- **No new unit tests.** Pure wiring: the only code change is the + callback argument at the `BddTargetInteractive_Run` call site, and + `BddTargetSwitchConfig_SetByName` is the same function the existing + `set transport` path in `OnSet` already calls. The end-to-end + `switching_transport.feature` scenario is the test for the dispatch. + +### Deferred + +- **TLS switching scenario (`@tls`-tagged second scenario of + `switching_transport.feature`)** — S08.07 (#272). +- **Unit test for the `BddTargetInteractive` `switch` dispatch.** The + `onSwitch` slot was added in the S20.x SwitchingSender stories + without a paired unit test in `BddTargetInteractiveTest`; the + existing tests cover the `set` path only. Out of scope here (pure + wiring story) but worth a hygiene follow-up — every other dispatch + in this file is pinned, so `switch`'s absence is the odd one out. +- **CMake-driven memory scaling** still open from S08.09 — memory + `project_freertos_stack_budget`. The S08.09 `INTERACTIVE_TASK_ + STACK_DEPTH = configMINIMAL_STACK_SIZE * 48U` already carries the + SwitchingSender + TCP path overhead, so no further bump here. + +### Open questions + +- None new. The S08.10 question about the 200ms SO_SNDTIMEO cap + potentially starving the Service task during outage is unrelated + to switching; CI is the arbiter and the answer is "raise the + deadline if it flakes" per `feedback_no_flaky_ci`. + ## 2026-05-11 — S08.10 tcp_reconnect BDD scenario on QEMU (#339) Second slice of S08.06. The reconnect-on-the-wire is already a property diff --git a/ci/docker-compose.bdd.yml b/ci/docker-compose.bdd.yml index e600fb1c..8eb54c96 100644 --- a/ci/docker-compose.bdd.yml +++ b/ci/docker-compose.bdd.yml @@ -106,6 +106,9 @@ services: # marks features that need a file-backed store (no FreeRTOS file # abstraction yet), while plain @buffered just means "uses the long-lived # interactive process" and is supported on FreeRTOS via QEMU's UART. + # S08.11 tags the non-@tls switching_transport scenario @udp @tcp so the + # existing (@udp or @tcp) clause admits it; the @tls sibling stays + # excluded until S08.07 lights up TLS on FreeRTOS. # @windows_wip excludes the Windows-only TCP singletask variant. command: behave --junit --junit-directory Bdd/junit --tags='not @wip and not @freertoswip and not @rtc and not @store and not @windows_wip and (@udp or @tcp)' Bdd/features/