Skip to content

refactor: S13.7 extract Datagram abstraction from UdpSender#139

Merged
DavidCozens merged 4 commits into
mainfrom
refactor/s13.7-datagram-abstraction
Apr 17, 2026
Merged

refactor: S13.7 extract Datagram abstraction from UdpSender#139
DavidCozens merged 4 commits into
mainfrom
refactor/s13.7-datagram-abstraction

Conversation

@DavidCozens

@DavidCozens DavidCozens commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Purpose

Closes #133. Extract UDP socket operations from UdpSender into a SolidSyslogDatagram vtable abstraction, with PosixDatagram as the first implementation. Preparatory refactoring for S13.4 (Winsock UDP support) — once Datagram is extracted, adding Windows support is just a new WinsockDatagram implementation.

Parent epic: #40. Depends on S13.6 (#132, already merged).

Change Description

Follows the same vtable/dispatch pattern as Sender, Buffer, Store, Resolver:

  • SolidSyslogDatagramDefinition.h — vtable struct with Open, SendTo, Close
  • SolidSyslogDatagram.h / .c — forward declaration and dispatch functions
  • SolidSyslogPosixDatagram.h / .c — POSIX implementation using socket/sendto/close
  • UdpSender refactored to accept a SolidSyslogDatagram* in its config; no longer touches sockets directly.

Design decisions (following S13.6 pattern):

  • Open and Close return void (not bool as the issue draft suggested). The existing UdpSender does not check the return values of socket() or close(), so adding a bool return would introduce behaviour that has no caller and no tests. Error handling is deferred to Epic E12: Error Handling #31. SendTo returns bool, preserving the existing sendto(...) >= 0 semantics.
  • Address ownership: the resolved struct sockaddr_in stays in UdpSender (which owns the resolver) and is passed through to SendTo on each call. The Datagram is stateless w.r.t. addressing. The POSIX sockaddr_in type still leaks through the Datagram interface — same situation as the Resolver — hiding it is deferred until Winsock or connection-lifecycle work demands it.
  • Singleton pattern: same as GetAddrInfoResolver. One Datagram per UdpSender. Limitation tracked in fix: validate numeric CLI args for --max-files and --max-file-size #110 / memory.

Pure refactor — no new functionality, no behaviour change.

Test Evidence

New testsTests/SolidSyslogPosixDatagramTest.cpp: 14 tests drove the PosixDatagram implementation via strict TDD (red/green for each behaviour). Covers Open (socket called, AF_INET, SOCK_DGRAM), SendTo (called once, buffer/length/flags/fd/address/addrlen propagation, return value on success and failure), and Close (called with correct fd).

Existing tests preservedSolidSyslogUdpSenderTest.cpp (22 tests across 3 test groups) remain unchanged in their assertions and test bodies. Only the setup/teardown wiring changed to create/destroy a PosixDatagram. Because SocketFake intercepts at the libc level, all existing assertions (SocketFake_SocketCallCount, SocketFake_LastPort, SocketFake_CloseCallCount, etc.) continue to pass, proving the refactor preserves behaviour.

Local CI — all presets green:

  • debug, clang-debug, sanitize: 498 tests pass
  • coverage: 100% on new files (SolidSyslogDatagram.c, SolidSyslogPosixDatagram.c); 99.9% overall (pre-existing gap in SolidSyslogFileStore.c, unrelated)
  • tidy, cppcheck: clean
  • clang-format --dry-run --Werror: clean
  • BDD: 14 features / 31 scenarios / 160 steps passed

Areas Affected

New:

  • Interface/SolidSyslogDatagram.h, SolidSyslogDatagramDefinition.h, SolidSyslogPosixDatagram.h
  • Source/SolidSyslogDatagram.c, SolidSyslogPosixDatagram.c
  • Tests/SolidSyslogPosixDatagramTest.cpp

Modified:

  • Interface/SolidSyslogUdpSender.h — config gains datagram field
  • Source/SolidSyslogUdpSender.c — delegates socket operations to injected Datagram; removed fd field
  • Tests/SolidSyslogUdpSenderTest.cpp — setup/teardown wire in PosixDatagram
  • Tests/Example/ExampleServiceThreadTest.cpp — same wiring update
  • Example/SingleTask/SolidSyslogExample.c, Example/Threaded/main.c — create/destroy PosixDatagram
  • Source/CMakeLists.txt, Tests/CMakeLists.txt — add new source files

Not affected: TcpSender — S13.8 will extract the Stream abstraction for TCP connection operations.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a modular datagram abstraction layer for flexible network communication
    • Added POSIX-compatible datagram implementation with complete lifecycle management
  • Tests

    • Added comprehensive test coverage for datagram operations and UDP sender integration

Extract UDP socket operations into a SolidSyslogDatagram vtable
abstraction, following the same pattern as S13.6 Resolver.
PosixDatagram provides socket/sendto/close for POSIX platforms.

UdpSender wiring will follow in a subsequent commit.

Open/Close return void (no error handling today — Epic #31).
SendTo returns bool, preserving the existing sendto >= 0 semantics.
Add datagram to UdpSenderConfig. Remove internal socket/sendto/close
and the fd field — UdpSender now calls through the injected Datagram.
Sender retains protocol logic and owns the resolved address; the
Datagram owns the socket resource lifecycle.

All existing UdpSender tests pass unchanged — SocketFake intercepts at
the libc level, so the observable behaviour through PosixDatagram is
identical.

Examples and ExampleServiceThreadTest will be updated in the next
commit.
Both examples and the ExampleServiceThreadTest now create a
PosixDatagram and pass it in UdpSenderConfig. TcpSender is unchanged
(S13.8 will extract the Stream abstraction).
@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR extracts UDP socket operations from SolidSyslogUdpSender into a SolidSyslogDatagram abstraction using a vtable pattern, provides a POSIX implementation (SolidSyslogPosixDatagram), and refactors UdpSender to accept an injected datagram instance at creation time instead of managing sockets directly.

Changes

Cohort / File(s) Summary
Datagram Abstraction Interface
Interface/SolidSyslogDatagram.h, Interface/SolidSyslogDatagramDefinition.h
Introduces datagram vtable abstraction with three operations: Open, SendTo (returns bool), and Close. Provides opaque forward declarations and function-pointer struct definition for C linkage.
POSIX Datagram Implementation
Interface/SolidSyslogPosixDatagram.h, Source/SolidSyslogPosixDatagram.c
Implements PosixDatagram using UDP sockets (socket, sendto, close). Provides Create/Destroy factory functions and internally manages socket lifecycle via static method handlers.
Datagram Dispatch Functions
Source/SolidSyslogDatagram.c
Implements thin wrapper functions that delegate to function pointers on the datagram instance (Open, SendTo, Close).
UdpSender Refactoring
Interface/SolidSyslogUdpSender.h, Source/SolidSyslogUdpSender.c
Removes direct socket management from UdpSender; adds datagram field to config struct; replaces socket()/sendto() calls with delegated SolidSyslogDatagram_* operations.
Build Configuration
Source/CMakeLists.txt, Tests/CMakeLists.txt
Adds new POSIX datagram source and test files to build when SOLIDSYSLOG_POSIX is enabled.
Examples
Example/SingleTask/SolidSyslogExample.c, Example/Threaded/main.c
Updates example code to create and pass SolidSyslogPosixDatagram instance to UdpSender config; adds corresponding teardown calls.
Tests
Tests/SolidSyslogPosixDatagramTest.cpp, Tests/SolidSyslogUdpSenderTest.cpp, Tests/Example/ExampleServiceThreadTest.cpp
Adds new test file for PosixDatagram lifecycle and socket operations; updates existing tests to create and inject datagram abstraction.

Sequence Diagram

sequenceDiagram
    participant UdpSender
    participant SolidSyslogDatagram as Datagram<br/>(Dispatch)
    participant PosixDatagram as PosixDatagram<br/>(Implementation)
    participant Socket as POSIX Socket

    Note over UdpSender,Socket: OLD: Direct socket management
    UdpSender->>Socket: socket(AF_INET, SOCK_DGRAM)
    Socket-->>UdpSender: fd

    Note over UdpSender,Socket: OLD: Send operation
    UdpSender->>Socket: sendto(fd, buffer, addr)
    Socket-->>UdpSender: bytes sent (>= 0)

    UdpSender->>Socket: close(fd)

    Note over UdpSender,Socket: NEW: Abstracted socket management
    UdpSender->>SolidSyslogDatagram: SolidSyslogDatagram_Open(datagram)
    SolidSyslogDatagram->>PosixDatagram: datagram->Open(self)
    PosixDatagram->>Socket: socket(AF_INET, SOCK_DGRAM)
    Socket-->>PosixDatagram: fd

    Note over UdpSender,Socket: NEW: Send operation
    UdpSender->>SolidSyslogDatagram: SolidSyslogDatagram_SendTo(datagram, buffer, addr)
    SolidSyslogDatagram->>PosixDatagram: datagram->SendTo(self, buffer, addr)
    PosixDatagram->>Socket: sendto(fd, buffer, addr)
    Socket-->>PosixDatagram: bytes sent
    PosixDatagram-->>SolidSyslogDatagram: bool (success)
    SolidSyslogDatagram-->>UdpSender: bool (success)

    UdpSender->>SolidSyslogDatagram: SolidSyslogDatagram_Close(datagram)
    SolidSyslogDatagram->>PosixDatagram: datagram->Close(self)
    PosixDatagram->>Socket: close(fd)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 Socket operations, once tangled in place,
Now hop through abstractions at elegant pace,
POSIX implementation leads the UDP way,
Windows can follow on a future day! 📦🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'refactor: S13.7 extract Datagram abstraction from UdpSender' clearly and concisely summarizes the main change: extracting UDP socket operations into a Datagram abstraction, which aligns with the changeset.
Description check ✅ Passed The pull request description comprehensively covers Purpose (links #133, S13.6 dependency), Change Description (vtable pattern, file structure, design decisions), Test Evidence (14 new tests, 22 existing preserved, CI green), and Areas Affected (lists all modified/new files). It fully addresses the template requirements.
Linked Issues check ✅ Passed The PR successfully addresses all acceptance criteria from #133: vtable definition and dispatch files created, PosixDatagram with Create/Destroy implemented, unit tests added (14 tests), UdpSender refactored with config gaining datagram field, existing tests preserved, CI passing, and examples updated. Pure refactor with no behavior change as required.
Out of Scope Changes check ✅ Passed All changes are in-scope and directly support #133 objectives: Datagram abstraction creation, PosixDatagram implementation, UdpSender refactoring, test updates, CMake adjustments, and example code updates. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/s13.7-datagram-abstraction

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

☀️   Quality Summary

   🚦   Unit Tests (GCC): 99% successful (✔️ 495 passed, 🙈 3 skipped)
   🚦   Unit Tests (Clang): 99% successful (✔️ 462 passed, 🙈 3 skipped)
   🚦   Unit Tests (Sanitize): 99% successful (✔️ 462 passed, 🙈 3 skipped)
   🚦   BDD Tests: 100% successful (✔️ 31 passed)
   🚦   Unit Tests (MSVC): 100% successful (✔️ 338 passed, 🙈 1 skipped)
   ⚠️   Clang-Tidy: No warnings
   ⚠️   CPPCheck: No warnings


Created by Quality Monitor v1.14.0 (#f3859fd). More details are shown in the GitHub Checks Result.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
Source/SolidSyslogPosixDatagram.c (1)

29-34: Make SolidSyslogPosixDatagram_Destroy defensively close any open socket.

Right now Destroy only nulls function pointers. If fd is still open, it can leak a descriptor.

Suggested patch
 void SolidSyslogPosixDatagram_Destroy(void)
 {
+    Close(&instance.base);
     instance.base.Open   = NULL;
     instance.base.SendTo = NULL;
     instance.base.Close  = NULL;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Source/SolidSyslogPosixDatagram.c` around lines 29 - 34,
SolidSyslogPosixDatagram_Destroy currently only nulls function pointers and can
leak the open socket; update it to check the instance.fd (or equivalent file
descriptor field on the instance object) and if it is >= 0 call close(fd)
(include the proper header if needed), then set instance.fd = -1 and finally
null out instance.base.Open/SendTo/Close as before to ensure the descriptor is
closed defensively; reference the SolidSyslogPosixDatagram_Destroy function and
the instance.fd field when making the change.
Tests/SolidSyslogPosixDatagramTest.cpp (1)

63-67: Optional: extract repeated Open + SendTo arrange/act into a helper.

This would reduce duplication and make intent in each test even clearer.

♻️ Suggested test refactor
 TEST_GROUP(SolidSyslogPosixDatagram)
 {
@@
+    void OpenAndSendDefault() const
+    {
+        SolidSyslogDatagram_Open(datagram);
+        SolidSyslogDatagram_SendTo(datagram, TEST_MESSAGE, TEST_MESSAGE_LEN, &addr);
+    }
@@
 TEST(SolidSyslogPosixDatagram, SendToCallsSendtoOnce)
 {
-    SolidSyslogDatagram_Open(datagram);
-    SolidSyslogDatagram_SendTo(datagram, TEST_MESSAGE, TEST_MESSAGE_LEN, &addr);
+    OpenAndSendDefault();
     LONGS_EQUAL(1, SocketFake_SendtoCallCount());
 }

Also applies to: 72-74, 79-81, 86-88, 93-95, 100-102, 107-109, 114-116

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Tests/SolidSyslogPosixDatagramTest.cpp` around lines 63 - 67, Extract the
repeated "open then send" setup into a small test helper to reduce duplication:
create a helper function (e.g., SendOpenAndSendTo or Arrange_OpenAndSendTo) that
calls SolidSyslogDatagram_Open(datagram) and
SolidSyslogDatagram_SendTo(datagram, TEST_MESSAGE, TEST_MESSAGE_LEN, &addr) and
use that helper in tests currently calling SolidSyslogDatagram_Open +
SolidSyslogDatagram_SendTo (tests referencing SendToCallsSendtoOnce and the
similar cases at lines noted) so each test becomes a single clear arrange/act
call followed by assertions like LONGS_EQUAL(1, SocketFake_SendtoCallCount()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Source/SolidSyslogPosixDatagram.c`:
- Around line 29-34: SolidSyslogPosixDatagram_Destroy currently only nulls
function pointers and can leak the open socket; update it to check the
instance.fd (or equivalent file descriptor field on the instance object) and if
it is >= 0 call close(fd) (include the proper header if needed), then set
instance.fd = -1 and finally null out instance.base.Open/SendTo/Close as before
to ensure the descriptor is closed defensively; reference the
SolidSyslogPosixDatagram_Destroy function and the instance.fd field when making
the change.

In `@Tests/SolidSyslogPosixDatagramTest.cpp`:
- Around line 63-67: Extract the repeated "open then send" setup into a small
test helper to reduce duplication: create a helper function (e.g.,
SendOpenAndSendTo or Arrange_OpenAndSendTo) that calls
SolidSyslogDatagram_Open(datagram) and SolidSyslogDatagram_SendTo(datagram,
TEST_MESSAGE, TEST_MESSAGE_LEN, &addr) and use that helper in tests currently
calling SolidSyslogDatagram_Open + SolidSyslogDatagram_SendTo (tests referencing
SendToCallsSendtoOnce and the similar cases at lines noted) so each test becomes
a single clear arrange/act call followed by assertions like LONGS_EQUAL(1,
SocketFake_SendtoCallCount()).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c15be25f-bec9-46ec-8a14-19070eb00b66

📥 Commits

Reviewing files that changed from the base of the PR and between 8895485 and ab1ffbe.

📒 Files selected for processing (14)
  • Example/SingleTask/SolidSyslogExample.c
  • Example/Threaded/main.c
  • Interface/SolidSyslogDatagram.h
  • Interface/SolidSyslogDatagramDefinition.h
  • Interface/SolidSyslogPosixDatagram.h
  • Interface/SolidSyslogUdpSender.h
  • Source/CMakeLists.txt
  • Source/SolidSyslogDatagram.c
  • Source/SolidSyslogPosixDatagram.c
  • Source/SolidSyslogUdpSender.c
  • Tests/CMakeLists.txt
  • Tests/Example/ExampleServiceThreadTest.cpp
  • Tests/SolidSyslogPosixDatagramTest.cpp
  • Tests/SolidSyslogUdpSenderTest.cpp

@DavidCozens

Copy link
Copy Markdown
Owner Author

Thanks for the review. Both nitpicks are deferred — rationale below.

PosixDatagram_Destroy defensive close (Source/SolidSyslogPosixDatagram.c)

Deferring. Before this refactor, UdpSender_Destroy defensively closed the fd. That behaviour is preserved — it now lives in PosixDatagram::Close(), which UdpSender_Destroy calls before tearing down the datagram. Through the only supported API path (UdpSender → Datagram) there is no leak and no regression.

The concern only materialises if user code uses PosixDatagram in isolation and calls Destroy without Close first — which isn't a supported pattern today and has no test. Adding defensive cleanup without a driving test violates the "no untested production code" discipline and the "don't add error handling that doesn't exist yet" principle we followed throughout S13.6 and S13.7. If standalone usage emerges, or as part of Epic #31 (error handling / robustness), the contract can be formalised with tests.

Test helper extraction (Tests/SolidSyslogPosixDatagramTest.cpp)

Deferring. Valid stylistic suggestion, but extracting the Open + SendTo preamble cleanly would require splitting into two TEST_GROUPs (Create/Open-only vs Open-in-setup) to match the UdpSender pattern. That's a non-trivial restructure for a mild duplication gain, and it belongs in its own style commit rather than bundled into a pure refactor PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

S13.07: Extract Datagram abstraction, provide PosixDatagram

1 participant