diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12b4e1ae..eeb05714 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: run: cmake --preset debug - name: Build and test - run: cmake --build --preset debug --target junit ExampleTests SolidSyslogExample SolidSyslogThreadedExample + run: cmake --build --preset debug --target junit ExampleTests SolidSyslogThreadedExample - name: Run example tests run: cd build/debug && ./Tests/Example/ExampleTests -ojunit -k ExampleTests @@ -56,7 +56,6 @@ jobs: with: name: solid-syslog-examples path: | - build/debug/Example/SolidSyslogExample build/debug/Example/SolidSyslogThreadedExample retention-days: 1 compression-level: 0 @@ -622,8 +621,8 @@ jobs: name: solid-syslog-examples path: build/debug/Example/ - - name: Make example binaries executable - run: chmod +x build/debug/Example/SolidSyslogExample build/debug/Example/SolidSyslogThreadedExample + - name: Make example binary executable + run: chmod +x build/debug/Example/SolidSyslogThreadedExample - name: Prepare BDD output directory run: mkdir -p Bdd/junit && chmod 777 Bdd/junit @@ -678,7 +677,7 @@ jobs: run: cmake --preset debug - name: Build and test - run: cmake --build --preset debug --target junit ExampleTests SolidSyslogExample SolidSyslogThreadedExample + run: cmake --build --preset debug --target junit ExampleTests SolidSyslogThreadedExample build-freertos-target: runs-on: ubuntu-latest diff --git a/Bdd/README.md b/Bdd/README.md index b6bce8a5..44c0e9a0 100644 --- a/Bdd/README.md +++ b/Bdd/README.md @@ -8,7 +8,7 @@ same feature files run on every target — the active runner is selected by the | `BDD_TARGET` | Example-under-test | Oracle | |---|---|---| -| `linux` (default) | `build/debug/Example/SolidSyslogExample` (native subprocess) | `syslog-ng-linux` | +| `linux` (default) | `build/debug/Example/SolidSyslogThreadedExample` (native subprocess) | `syslog-ng-linux` | | `windows` | `build/msvc-debug/Example/Debug/SolidSyslogExample.exe` (native subprocess) | `otelcol-contrib` | | `freertos` | `build/freertos-cross/Example/FreeRtos/SingleTask/SolidSyslogFreeRtosSingleTask.elf` driven through `qemu-system-arm` | `syslog-ng-freertos` | diff --git a/Bdd/features/environment.py b/Bdd/features/environment.py index b5819763..dff7871e 100644 --- a/Bdd/features/environment.py +++ b/Bdd/features/environment.py @@ -100,7 +100,7 @@ def before_all(context): context.target = os.environ.get("BDD_TARGET", "linux").lower() default_binaries = { - "linux": "build/debug/Example/SolidSyslogExample", + "linux": "build/debug/Example/SolidSyslogThreadedExample", "windows": "build/msvc-debug/Example/Debug/SolidSyslogExample.exe", "freertos": ( "build/freertos-cross/Example/FreeRtos/SingleTask/" diff --git a/Bdd/features/steps/syslog_steps.py b/Bdd/features/steps/syslog_steps.py index 998b0988..1b6045d9 100644 --- a/Bdd/features/steps/syslog_steps.py +++ b/Bdd/features/steps/syslog_steps.py @@ -53,10 +53,11 @@ def per_transport_log(context, transport): return PER_TRANSPORT_LOG_OTEL[transport] return PER_TRANSPORT_LOG_SYSLOG_NG[transport] -# POSIX-only paths used by the @buffered/threaded scenarios. The cross-platform -# scenarios use context.example_binary / context.received_log instead, set in -# environment.before_all from EXAMPLE_BINARY / RECEIVED_LOG / ORACLE_FORMAT. -THREADED_BINARY = "build/debug/Example/SolidSyslogThreadedExample" +# Paths used by the threaded/buffered scenarios. context.example_binary +# (set in environment.before_all from EXAMPLE_BINARY / per-target default) is +# the single binary path on every runner now — S24.04 collapsed Linux onto the +# Threaded binary, matching Windows (single buffered example since S13.20) and +# FreeRTOS (single buffered example since S08.04). SYSLOG_NG_CTL = "/var/lib/syslog-ng/syslog-ng.ctl" SYSLOG_NG_CONF = "Bdd/syslog-ng/syslog-ng.conf" SYSLOG_NG_FULL_CONF = "Bdd/syslog-ng/syslog-ng-full.conf" @@ -386,35 +387,42 @@ def wait_for_messages(context, expected_messages): context.message_count = len(context.all_lines) -def run_example(context, extra_args=None, binary=None, expected_messages=1): - """Run the example via the prompt protocol — works for both NullBuffer - (synchronous send, Linux SingleTask) and CircularBuffer + service thread - (asynchronous, Windows buffered example post-S13.18). +def run_example(context, extra_args=None, expected_messages=1): + """Run the example via the prompt protocol. + + Every supported runner ships a single buffered example binary now — + Linux (pthread + PosixMessageQueueBuffer, S24.04), Windows (Win32 + + CircularBuffer, S13.20), FreeRTOS-on-QEMU (Service task + + CircularBuffer, S08.04) — so context.example_binary is the only + binary path on any target. + + Pin --app-name "SolidSyslogExample" on Linux/Windows so the RFC 5424 + APP-NAME field stays runner-independent. Without it, Linux records + would carry the binary basename "SolidSyslogThreadedExample" and + Windows would carry "SolidSyslogExample", breaking the + `the app name is "SolidSyslogExample"` assertions in the + platform-agnostic features. FreeRTOS hardcodes the same string in + Example/FreeRtos/SingleTask/main.c and doesn't accept --app-name, so + skip the flag on that runner. The pre-S13.18 implementation wrote `send N\\nquit\\n` upfront via process.communicate() and relied on NullBuffer's synchronous Send to - guarantee delivery before exit. That assumption broke on Windows once - SolidSyslogExample.exe became buffered — `quit` could land before the - service thread had drained, losing the UDP packet. The prompt protocol + guarantee delivery before exit. That assumption broke once the + example became buffered — `quit` could land before the service + thread had drained, losing the UDP packet. The prompt protocol coordinates around it: wait for the oracle to confirm receipt before sending `quit`. """ - binary = binary or context.example_binary - _run_with_prompt_protocol(context, binary, "Example", extra_args, expected_messages) - - -def _run_with_prompt_protocol(context, binary, label, extra_args, expected_messages): - """Run a binary that speaks the SolidSyslog> prompt protocol. - - Shared body for run_threaded_example (Linux pthread binary) and - run_buffered_example (cross-platform — Linux pthread or Windows Win32 - threaded). The protocol is: wait initial prompt, send "send N", wait - next prompt, wait until oracle has confirmed N messages, then "quit". - """ + binary = context.example_binary assert os.path.exists(binary), ( - f"{label} binary not found at {binary} — build with cmake first" + f"Example binary not found at {binary} — build with cmake first" ) + if context.target != "freertos": + extra_args = list(extra_args or []) + if "--app-name" not in extra_args: + extra_args += ["--app-name", "SolidSyslogExample"] + process = spawn_example_process(context, extra_args=extra_args, binary=binary) context.example_pid = process.pid @@ -431,7 +439,7 @@ def _run_with_prompt_protocol(context, binary, label, extra_args, expected_messa # binaries exit cleanly on `quit` and their return code is # meaningful. assert returncode in (0, None), ( - f"{label} failed with exit code {returncode}" + f"Example failed with exit code {returncode}" ) finally: # Don't let an intermediate exception leak the helper into later @@ -441,34 +449,6 @@ def _run_with_prompt_protocol(context, binary, label, extra_args, expected_messa process.wait(timeout=5) -def run_threaded_example(context, extra_args=None, expected_messages=1): - """Run the Linux pthread-driven Threaded example via the prompt protocol. - Used for features that are pthread-specific (file store, switching, - syslog-ng reload), which stay Linux-only and keep "the threaded example" - prose in their .feature files. - """ - _run_with_prompt_protocol( - context, THREADED_BINARY, "Threaded example", extra_args, expected_messages - ) - - -def run_buffered_example(context, extra_args=None, expected_messages=1): - """Run the buffered example via the prompt protocol — cross-platform. - - Linux runner (syslog-ng oracle): the pthread-driven Threaded example. - Windows runner (OTel oracle): the Win32-thread-driven example wired by - S13.18 — context.example_binary already points at it. - FreeRTOS-on-QEMU runner (syslog-ng oracle): the SingleTask example is - itself buffered (CircularBuffer + FreeRtosMutex + Service task, S08.04) - — context.example_binary already points at the .elf. Lets the same - .feature scenario exercise the buffering path on all three runners. - """ - binary = THREADED_BINARY if context.target == "linux" else context.example_binary - _run_with_prompt_protocol( - context, binary, "Buffered example", extra_args, expected_messages - ) - - def syslog_ng_reload(): """Send RELOAD to syslog-ng via its Unix control socket.""" with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: @@ -530,8 +510,10 @@ def build_threaded_command(context, transport, no_sd=False): Linux runner (syslog-ng oracle): the pthread-driven Threaded example. Windows runner (OTel oracle): the unified SolidSyslogExample.exe — its BlockStore + SwitchingSender wiring (S13.20) accepts the same flags. + Both resolve through context.example_binary now that S24.04 retired the + Linux SingleTask binary. """ - binary = THREADED_BINARY if context.oracle_format == "syslog-ng" else context.example_binary + binary = context.example_binary assert os.path.exists(binary), ( f"Threaded binary not found at {binary} — build with cmake first" ) @@ -778,32 +760,32 @@ def step_example_sends_with_transport(context, transport): @when("the threaded example sends a syslog message") def step_threaded_sends_message(context): - run_threaded_example(context) + run_example(context) @when("the threaded example sends a syslog message with transport {transport}") def step_threaded_sends_with_transport(context, transport): - run_threaded_example(context, ["--transport", transport]) + run_example(context, ["--transport", transport]) @when("the threaded example sends {count:d} syslog messages") def step_threaded_sends_multiple(context, count): - run_threaded_example(context, expected_messages=count) + run_example(context, expected_messages=count) @when("the buffered example sends a syslog message") def step_buffered_sends_message(context): - run_buffered_example(context) + run_example(context) @when("the buffered example sends a syslog message with transport {transport}") def step_buffered_sends_with_transport(context, transport): - run_buffered_example(context, ["--transport", transport]) + run_example(context, ["--transport", transport]) @when("the buffered example sends {count:d} syslog messages") def step_buffered_sends_multiple(context, count): - run_buffered_example(context, expected_messages=count) + run_example(context, expected_messages=count) @when("the example program sends a message with facility {facility:d} and severity {severity:d}") diff --git a/Bdd/features/steps/target_driver.py b/Bdd/features/steps/target_driver.py index 8525ea53..d128de50 100644 --- a/Bdd/features/steps/target_driver.py +++ b/Bdd/features/steps/target_driver.py @@ -70,13 +70,11 @@ def spawn_example_process(context, extra_args=None, binary=None): commands instead — they are delivered via apply_extra_args after the prompt is up. - binary defaults to context.example_binary (the standard SingleTask - binary or its FreeRTOS .elf equivalent); callers that drive a - different example (e.g. the Linux Threaded binary) pass it - explicitly. On FreeRTOS, the SingleTask .elf is itself buffered - (CircularBuffer + FreeRtosMutex + Service task, S08.04) so it - serves both the @buffered scenarios and the plain single-message - path — there is no separate FreeRTOS threaded binary. + binary defaults to context.example_binary. Each supported runner + ships a single buffered example binary now — Linux's pthread + Threaded example (S24.04), Windows's Win32 buffered example + (S13.20), and the FreeRTOS CircularBuffer + Service-task .elf + (S08.04) — so callers don't override the default in normal use. """ target = getattr(context, "target", "linux") if binary is None: diff --git a/CLAUDE.md b/CLAUDE.md index ced7022b..213ea6c1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -278,7 +278,7 @@ Coverage report: `cmake --preset coverage && cmake --build --preset coverage --t Core/Interface/ — Public headers of the core library. No implementation. This is the API boundary. Core/Source/ — Core library implementation. Compiled into a static library. Platform/ — Platform-specific code (Posix, Windows, OpenSsl) — each a subfolder with its own Interface/ and Source/. -Example/ — Example applications (Common, SingleTask, Threaded). +Example/ — Example applications (Common, Threaded, Windows, FreeRtos). Tests/ — CppUTest unit tests. Never link production code directly; always via the library. Tests/Support/ — PosixFakes static lib (SocketFake, ClockFake) — shared across test executables. Tests/Example/ — Example code unit tests (ExampleTests executable). diff --git a/DEVLOG.md b/DEVLOG.md index db2c425e..92f2419a 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -6036,3 +6036,68 @@ tag in the same pass. `SOLIDSYSLOG_MAX_MESSAGE_SIZE` / SD-count knobs in the resource-sizing backlog? Trivial to change later, but worth flagging for the resource- sizing epic when it lands. + +## 2026-05-11 — S24.04 retire Linux SingleTask binary + +### Decisions + +- **Linux BDD now drives the Threaded binary by default.** + `environment.before_all`'s `BDD_TARGET=linux` default flipped from + `build/debug/Example/SolidSyslogExample` to + `build/debug/Example/SolidSyslogThreadedExample`. The Threaded + binary's SwitchingSender + BlockStore wiring is a strict superset of + what the SingleTask NullBuffer binary covered after S15, so the + cross-platform `the example program sends ...` scenarios run + unchanged. Windows BDD already collapsed to one binary in S13.20, + FreeRTOS in S08.04 slice 2 — Linux was the last platform carrying + two. +- **Pin `--app-name "SolidSyslogExample"` in `run_example` for + non-FreeRTOS targets.** Without it Linux records would carry the + basename `SolidSyslogThreadedExample` and Windows would carry + `SolidSyslogExample`, breaking the platform-agnostic + `the app name is "SolidSyslogExample"` assertions. FreeRTOS + hardcodes the same string in its example main and has no `getopt` + port, so skip the injection on that runner. Lighter touch than + rewriting the feature lines and keeps `SolidSyslogExample` as the + user-visible APP-NAME until S24.05 finishes the rename pass. +- **Collapsed `run_example` / `run_threaded_example` / + `run_buffered_example` into a single helper.** The three feature + phrasings — *the example program*, *the threaded example*, *the + buffered example* — stay distinct in `.feature` prose so the + scenario intent is still readable, but all three Python decorators + now dispatch to `run_example`. Same for `build_threaded_command`, + which dropped its `oracle_format` branch. `THREADED_BINARY` + constant gone. +- **Pre-flip audit confirmed no implicit-synchronous-send fragility + in `run_example` callers.** Every caller already routes through + `_run_with_prompt_protocol` → `wait_for_messages`, which polls + oracle-receipt before issuing `quit`. NullBuffer's synchronous-send + semantics were never load-bearing on the assertions; the prompt + protocol coordinates the buffered drain. Confirmed locally by + re-running the Linux BDD suite (`not @wip and not @windows_wip and + not @freertoswip and not @no_rtc`): 44 scenarios pass. +- **Deleted `Example/SingleTask/` and + `Tests/Example/SolidSyslogExampleTest.cpp`** plus the matching + CMake entries and CI workflow references (`SolidSyslogExample` + target name, artifact path, `chmod +x` step). Existing + `analyze-format` / `analyze-tidy` / `analyze-cppcheck` / `sanitize` + / `coverage` (99.5% lines, 98.9% functions) presets all green + locally. + +### Deferred + +- Renaming or moving the Threaded binary out of `Example/` — that's + S24.05 (issue #333), which depends on this story landing first. +- DEVLOG and historical blog references to the SingleTask binary + remain untouched per *Never rewrite history*. Live docs + (`README.md`, `Bdd/README.md`, `docs/bdd.md`, `CLAUDE.md`) updated + to reflect the post-S24.04 layout. + +### Open questions + +- None — the residual `SolidSyslogExample` string mentions in the + repo are all either the Windows binary's `OUTPUT_NAME`, the + hardcoded `OriginSdConfig.software` value, the FreeRTOS app-name + global, or the `--app-name` pin in `run_example`. They are not + references to the (now-deleted) SingleTask binary and S24.05's + rename pass will revisit them. diff --git a/Example/CMakeLists.txt b/Example/CMakeLists.txt index a022df7e..1c396374 100644 --- a/Example/CMakeLists.txt +++ b/Example/CMakeLists.txt @@ -12,17 +12,12 @@ if(SOLIDSYSLOG_POSIX) Common/ExampleSwitchConfig.c ) - # Single-task example: NullBuffer, direct send, bare-metal model - add_executable(SolidSyslogExample SingleTask/main.c SingleTask/SolidSyslogExample.c ${COMMON_SOURCES}) - target_link_libraries(SolidSyslogExample PRIVATE ${PROJECT_NAME}) - target_include_directories(SolidSyslogExample PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/SingleTask - ${CMAKE_CURRENT_SOURCE_DIR}/Common - ) - # Threaded example: PosixMessageQueueBuffer, two pthreads (requires POSIX threads and mqueue). # TLS transport is selected at link time via Common/ExampleTlsSender_.c # — OpenSSL+PosixTcp when SOLIDSYSLOG_OPENSSL, nil-object stub otherwise. + # This is the only Linux example binary since S24.04 retired the + # SingleTask + NullBuffer variant — its SwitchingSender + BlockStore + # wiring is a strict superset of what SingleTask used to provide. set(THREADED_SOURCES Threaded/main.c ${COMMON_SOURCES}) if(SOLIDSYSLOG_OPENSSL) list(APPEND THREADED_SOURCES diff --git a/Example/SingleTask/SolidSyslogExample.c b/Example/SingleTask/SolidSyslogExample.c deleted file mode 100644 index c8508630..00000000 --- a/Example/SingleTask/SolidSyslogExample.c +++ /dev/null @@ -1,147 +0,0 @@ -#include "SolidSyslogExample.h" - -#include -#include -#include - -#include "ExampleAppName.h" -#include "ExampleCommandLine.h" -#include "ExampleEnterpriseId.h" -#include "ExampleInteractive.h" -#include "ExampleIps.h" -#include "ExampleLanguage.h" -#include "ExampleStderrErrorHandler.h" -#include "ExampleTcpConfig.h" -#include "ExampleUdpConfig.h" -#include "SolidSyslog.h" -#include "SolidSyslogAtomicCounter.h" -#include "SolidSyslogConfig.h" -#include "SolidSyslogGetAddrInfoResolver.h" -#include "SolidSyslogMetaSd.h" -#include "SolidSyslogNullBuffer.h" -#include "SolidSyslogNullStore.h" -#include "SolidSyslogOriginSd.h" -#include "SolidSyslogPosixClock.h" -#include "SolidSyslogPosixDatagram.h" -#include "SolidSyslogPosixHostname.h" -#include "SolidSyslogPosixProcessId.h" -#include "SolidSyslogPosixSysUpTime.h" -#include "SolidSyslogPosixTcpStream.h" -#include "SolidSyslogStreamSender.h" -#include "SolidSyslogTimeQualitySd.h" -#include "SolidSyslogUdpSender.h" -#include "SolidSyslogTimeQuality.h" - -static SolidSyslogPosixTcpStreamStorage tcpStreamStorage; -static SolidSyslogStreamSenderStorage tcpSenderStorage; - -static void GetTimeQuality(struct SolidSyslogTimeQuality* timeQuality) -{ - timeQuality->tzKnown = true; - timeQuality->isSynced = true; - timeQuality->syncAccuracyMicroseconds = SOLIDSYSLOG_SYNC_ACCURACY_OMIT; -} - -int SolidSyslogExample_Run(int argc, char* argv[]) -{ - ExampleStderrErrorHandler_Install(); - ExampleAppName_Set(argv[0]); - - struct ExampleOptions options; - if (ExampleCommandLine_Parse(argc, argv, &options) != 0) - { - return 1; - } - - struct SolidSyslogResolver* resolver = SolidSyslogGetAddrInfoResolver_Create(); - struct SolidSyslogDatagram* datagram = NULL; - struct SolidSyslogStream* stream = NULL; - struct SolidSyslogSender* sender = NULL; - bool useTcp = (options.transport != NULL) && (strcmp(options.transport, "tcp") == 0); - - if (useTcp) - { - stream = SolidSyslogPosixTcpStream_Create(&tcpStreamStorage); - struct SolidSyslogStreamSenderConfig tcpConfig = { - .resolver = resolver, - .stream = stream, - .endpoint = ExampleTcpConfig_GetEndpoint, - .endpointVersion = ExampleTcpConfig_GetEndpointVersion, - }; - sender = SolidSyslogStreamSender_Create(&tcpSenderStorage, &tcpConfig); - } - else - { - datagram = SolidSyslogPosixDatagram_Create(); - struct SolidSyslogUdpSenderConfig udpConfig = { - .resolver = resolver, - .datagram = datagram, - .endpoint = ExampleUdpConfig_GetEndpoint, - .endpointVersion = ExampleUdpConfig_GetEndpointVersion, - }; - sender = SolidSyslogUdpSender_Create(&udpConfig); - } - struct SolidSyslogBuffer* buffer = SolidSyslogNullBuffer_Create(sender); - struct SolidSyslogStore* store = SolidSyslogNullStore_Create(); - struct SolidSyslogAtomicCounter* counter = SolidSyslogAtomicCounter_Create(); - struct SolidSyslogMetaSdConfig metaConfig = { - .counter = counter, - .getSysUpTime = SolidSyslogPosixSysUpTime_Get, - .getLanguage = ExampleLanguage_Get, - }; - struct SolidSyslogStructuredData* metaSd = SolidSyslogMetaSd_Create(&metaConfig); - struct SolidSyslogStructuredData* timeQuality = SolidSyslogTimeQualitySd_Create(GetTimeQuality); - struct SolidSyslogOriginSdConfig originConfig = { - .software = "SolidSyslogExample", - .swVersion = "0.7.0", - .enterpriseId = EXAMPLE_ENTERPRISE_ID, - .getIpCount = ExampleIps_Count, - .getIpAt = ExampleIps_At, - }; - struct SolidSyslogStructuredData* originSd = SolidSyslogOriginSd_Create(&originConfig); - - struct SolidSyslogStructuredData* sdList[] = {metaSd, timeQuality, originSd}; - - struct SolidSyslogConfig config = { - .buffer = buffer, - .sender = NULL, - .clock = SolidSyslogPosixClock_GetTimestamp, - .getHostname = SolidSyslogPosixHostname_Get, - .getAppName = ExampleAppName_Get, - .getProcessId = SolidSyslogPosixProcessId_Get, - .store = store, - .sd = sdList, - .sdCount = sizeof(sdList) / sizeof(sdList[0]), - }; - SolidSyslog_Create(&config); - - struct SolidSyslogMessage message = { - .facility = options.facility, - .severity = options.severity, - .messageId = options.messageId, - .msg = options.msg, - }; - - ExampleInteractive_Run(&message, stdin, NULL, NULL); - - SolidSyslog_Destroy(); - SolidSyslogOriginSd_Destroy(); - SolidSyslogTimeQualitySd_Destroy(); - SolidSyslogMetaSd_Destroy(); - SolidSyslogAtomicCounter_Destroy(); - SolidSyslogNullStore_Destroy(); - SolidSyslogNullBuffer_Destroy(); - if (useTcp) - { - SolidSyslogStreamSender_Destroy(sender); - SolidSyslogPosixTcpStream_Destroy(stream); - } - else - { - SolidSyslogUdpSender_Destroy(); - SolidSyslogPosixDatagram_Destroy(); - } - SolidSyslogGetAddrInfoResolver_Destroy(); - - return 0; -} diff --git a/Example/SingleTask/SolidSyslogExample.h b/Example/SingleTask/SolidSyslogExample.h deleted file mode 100644 index 062f1b7c..00000000 --- a/Example/SingleTask/SolidSyslogExample.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef SOLIDSYSLOGEXAMPLE_H -#define SOLIDSYSLOGEXAMPLE_H - -#include "ExternC.h" - -EXTERN_C_BEGIN - - int SolidSyslogExample_Run(int argc, char* argv[]); - -EXTERN_C_END - -#endif /* SOLIDSYSLOGEXAMPLE_H */ diff --git a/Example/SingleTask/main.c b/Example/SingleTask/main.c deleted file mode 100644 index 5756c302..00000000 --- a/Example/SingleTask/main.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "SolidSyslogExample.h" - -int main(int argc, char* argv[]) -{ - return SolidSyslogExample_Run(argc, argv); -} diff --git a/README.md b/README.md index ac3465ab..1ff88222 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,8 @@ Public headers are split by audience (Interface Segregation Principle): - **`SolidSyslogPosixClock.h`** / **`SolidSyslogPosixHostname.h`** / **`SolidSyslogPosixProcessId.h`** / **`SolidSyslogPosixSysUpTime.h`** — POSIX helpers - **`SolidSyslogFreeRtosDatagram.h`** / **`SolidSyslogFreeRtosStaticResolver.h`** / **`SolidSyslogFreeRtosMutex.h`** / **`SolidSyslogFreeRtosSysUpTime.h`** — FreeRTOS adapters: FreeRTOS-Plus-TCP UDP datagram, hardcoded-IPv4 resolver, `xSemaphoreCreateMutexStatic`-backed mutex for CircularBuffer, and a kernel-tick sysUpTime source -Four example programs demonstrate usage: +Three example programs demonstrate usage: -- **`Example/SingleTask/`** — POSIX, NullBuffer, single-task bare-metal model - **`Example/Threaded/`** — POSIX, PosixMessageQueueBuffer, two pthreads (logger + service), SwitchingSender over UDP + TCP + TLS + mTLS (TLS build required for the last two); `--transport` sets the initial transport, `switch ` flips it at runtime - **`Example/Windows/`** — Windows, CircularBuffer + WindowsMutex, Win32 service thread (`_beginthreadex`) draining the buffer, Winsock UDP / TCP, with the Windows clock / hostname / process-id / sysUpTime helpers - **`Example/FreeRtos/SingleTask/`** — FreeRTOS-on-QEMU (Cortex-M3, mps2-an385), CircularBuffer + FreeRtosMutex drained by a dedicated Service task, UDP via FreeRTOS-Plus-TCP, interactive `set NAME VALUE` / `send N` / `quit` command channel over the CMSDK UART; BDD-driven against syslog-ng. See [`Example/FreeRtos/README.md`](Example/FreeRtos/README.md) diff --git a/Tests/Example/CMakeLists.txt b/Tests/Example/CMakeLists.txt index 7d5acd69..67c8d1c3 100644 --- a/Tests/Example/CMakeLists.txt +++ b/Tests/Example/CMakeLists.txt @@ -20,12 +20,10 @@ if(SOLIDSYSLOG_POSIX) list(APPEND EXAMPLE_TEST_SOURCES ExampleCommandLineTest.cpp ExampleInteractiveTest.cpp - SolidSyslogExampleTest.cpp ExampleServiceThreadTest.cpp ExampleSwitchConfigTest.cpp ) list(APPEND EXAMPLE_PRODUCTION_SOURCES - ${CMAKE_SOURCE_DIR}/Example/SingleTask/SolidSyslogExample.c ${CMAKE_SOURCE_DIR}/Example/Common/ExampleCommandLine.c ${CMAKE_SOURCE_DIR}/Example/Common/ExampleServiceThread.c ${CMAKE_SOURCE_DIR}/Example/Common/ExampleStderrErrorHandler.c @@ -34,9 +32,6 @@ if(SOLIDSYSLOG_POSIX) ${CMAKE_SOURCE_DIR}/Example/Common/ExampleInteractive.c ${CMAKE_SOURCE_DIR}/Example/Common/ExampleSwitchConfig.c ) - list(APPEND EXAMPLE_TEST_INCLUDE_DIRS - ${CMAKE_SOURCE_DIR}/Example/SingleTask - ) endif() if(SOLIDSYSLOG_WINSOCK AND HAVE_WINDOWS_PLATFORM) diff --git a/Tests/Example/SolidSyslogExampleTest.cpp b/Tests/Example/SolidSyslogExampleTest.cpp deleted file mode 100644 index 47e1d844..00000000 --- a/Tests/Example/SolidSyslogExampleTest.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include -#include -#include -#include - -#include "SolidSyslogExample.h" -#include "ClockFake.h" -#include "SocketFake.h" -#include "TestUtils.h" -#include "CppUTest/TestHarness.h" - -using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-file scope only; brings NEVER/ONCE/TWICE/THRICE into scope for the CALLED_* - // macros - -static const char* const STDIN_SEND_ONE = "/tmp/solidsyslog_test_send1.txt"; -static const char* const STDIN_SEND_THREE = "/tmp/solidsyslog_test_send3.txt"; - -// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -- path and content are semantically distinct -static void CreateInputFile(const char* path, const char* content) -{ - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) -- fopen/fclose is POSIX C; no owning memory concern - FILE* f = std::fopen(path, "w"); - // NOLINTNEXTLINE(clang-analyzer-unix.Stream) -- test helper; fopen failure is a test infrastructure error - std::fputs(content, f); - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) -- fclose is POSIX C; no owning memory concern - std::fclose(f); -} - -static void RedirectStdin(const char* path) -{ - // cppcheck-suppress ignoredReturnValue -- test helper; freopen failure is a test infrastructure error - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) -- freopen redirects stdin for test; no ownership transfer - std::freopen(path, "r", stdin); -} - -// clang-format off -TEST_GROUP(SolidSyslogExample) -{ - void setup() override - { - SocketFake_Reset(); - ClockFake_Reset(); - ClockFake_SetTime(1743768600, 0); - optind = 1; - CreateInputFile(STDIN_SEND_ONE, "send\nquit\n"); - CreateInputFile(STDIN_SEND_THREE, "send 3\nquit\n"); - } - - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) -- CppUTest TEST_GROUP method - int Run(int argc, char* argv[]) - { - return SolidSyslogExample_Run(argc, argv); - } - - int RunWithNoArgs() - { - RedirectStdin(STDIN_SEND_ONE); - char arg0[] = "SolidSyslogExample"; - char* argv[] = {arg0, nullptr}; - return Run(1, argv); - } -}; - -// clang-format on - -TEST(SolidSyslogExample, InvalidOptionReturnsOne) -{ - char arg0[] = "SolidSyslogExample"; - char arg1[] = "--invalid"; - char* argv[] = {arg0, arg1, nullptr}; - LONGS_EQUAL(1, Run(2, argv)); -} - -TEST(SolidSyslogExample, InvalidStoreReturnsOne) -{ - char arg0[] = "SolidSyslogExample"; - char arg1[] = "--store"; - char arg2[] = "invalid"; - char* argv[] = {arg0, arg1, arg2, nullptr}; - LONGS_EQUAL(1, Run(3, argv)); -} - -TEST(SolidSyslogExample, RunWithNoArgsReturnsZero) -{ - LONGS_EQUAL(0, RunWithNoArgs()); -} - -TEST(SolidSyslogExample, RunSendsOneMessage) -{ - RunWithNoArgs(); - CALLED_FAKE(SocketFake_Sendto, ONCE); -} - -TEST(SolidSyslogExample, DefaultMessageContainsLocal0InfoPrival) -{ - RunWithNoArgs(); - STRNCMP_EQUAL("<134>", SocketFake_LastBufAsString(), 5); -} - -TEST(SolidSyslogExample, FacilityFlagAppearsInPrival) -{ - RedirectStdin(STDIN_SEND_ONE); - char arg0[] = "SolidSyslogExample"; - char arg1[] = "--facility"; - char arg2[] = "0"; - char* argv[] = {arg0, arg1, arg2, nullptr}; - Run(3, argv); - STRNCMP_EQUAL("<6>", SocketFake_LastBufAsString(), 3); -} - -TEST(SolidSyslogExample, SeverityFlagAppearsInPrival) -{ - RedirectStdin(STDIN_SEND_ONE); - char arg0[] = "SolidSyslogExample"; - char arg1[] = "--severity"; - char arg2[] = "0"; - char* argv[] = {arg0, arg1, arg2, nullptr}; - Run(3, argv); - STRNCMP_EQUAL("<128>", SocketFake_LastBufAsString(), 5); -} - -TEST(SolidSyslogExample, ResolvesConfiguredHost) -{ - RunWithNoArgs(); - STRCMP_EQUAL("syslog-ng", SocketFake_LastGetAddrInfoHostname()); -} - -TEST(SolidSyslogExample, SendsToConfiguredPort) -{ - RunWithNoArgs(); - LONGS_EQUAL(5514, SocketFake_LastPort()); -} - -TEST(SolidSyslogExample, AppNameDerivedFromArgv0) -{ - RedirectStdin(STDIN_SEND_ONE); - char arg0[] = "/usr/bin/MyApp"; - char* argv[] = {arg0, nullptr}; - Run(1, argv); - const char* msg = SocketFake_LastBufAsString(); - CHECK(strstr(msg, "MyApp") != nullptr); -} - -TEST(SolidSyslogExample, SocketCreatedWithUdpDgram) -{ - RunWithNoArgs(); - LONGS_EQUAL(AF_INET, SocketFake_SocketDomain()); - LONGS_EQUAL(SOCK_DGRAM, SocketFake_SocketType()); -} - -TEST(SolidSyslogExample, SocketClosedAfterRun) -{ - RunWithNoArgs(); - CALLED_FAKE(SocketFake_Close, ONCE); -} - -TEST(SolidSyslogExample, MsgIdFlagAppearsInMessage) -{ - RedirectStdin(STDIN_SEND_ONE); - char arg0[] = "SolidSyslogExample"; - char arg1[] = "--msgid"; - char arg2[] = "ID47"; - char* argv[] = {arg0, arg1, arg2, nullptr}; - Run(3, argv); - CHECK(std::strstr(SocketFake_LastBufAsString(), "ID47") != nullptr); -} - -TEST(SolidSyslogExample, SendCommandSendsMultipleMessages) -{ - RedirectStdin(STDIN_SEND_THREE); - char arg0[] = "SolidSyslogExample"; - char* argv[] = {arg0, nullptr}; - Run(1, argv); - CALLED_FAKE(SocketFake_Sendto, THRICE); -} - -TEST(SolidSyslogExample, MessageFlagAppearsInMessage) -{ - RedirectStdin(STDIN_SEND_ONE); - char arg0[] = "SolidSyslogExample"; - char arg1[] = "--message"; - char arg2[] = "system started"; - char* argv[] = {arg0, arg1, arg2, nullptr}; - Run(3, argv); - CHECK(std::strstr(SocketFake_LastBufAsString(), "system started") != nullptr); -} diff --git a/docs/bdd.md b/docs/bdd.md index 89082d34..cd3aa837 100644 --- a/docs/bdd.md +++ b/docs/bdd.md @@ -65,7 +65,7 @@ through the QEMU UART (`-serial stdio`). | `ghcr.io/davidcozens/behave` | GHCR image — Debian trixie + Python + Behave ([source](https://github.com/DavidCozens/BehaveDocker)) | | `Bdd/output/` | Shared directory — syslog-ng writes here, Behave reads | | `Bdd/features/` | Gherkin feature files and step definitions | -| `Example/SolidSyslogExample.c` | Minimal C program that creates a logger and sends one message. Accepts `--facility` and `--severity` CLI flags (defaults to local0/info) | +| `Example/Threaded/main.c` | Buffered example: PosixMessageQueueBuffer + service pthread + SwitchingSender. Accepts `--facility`, `--severity`, `--transport`, `--app-name`, store-config flags | | `Example/CMakeLists.txt` | Builds the example binary, linked against the SolidSyslog library | ## syslog-ng configuration