Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Core/Interface/SolidSyslogPassthroughBufferErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ EXTERN_C_BEGIN
{
PASSTHROUGHBUFFER_ERROR_POOL_EXHAUSTED,
PASSTHROUGHBUFFER_ERROR_UNKNOWN_DESTROY,
PASSTHROUGHBUFFER_ERROR_NULL_SENDER,
PASSTHROUGHBUFFER_ERROR_MAX
};

Expand Down
2 changes: 2 additions & 0 deletions Core/Source/SolidSyslogPassthroughBufferMessages.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ static const char* PassthroughBufferError_AsString(uint8_t code)
"SolidSyslogPassthroughBuffer_Create pool exhausted; returning fallback buffer",
[PASSTHROUGHBUFFER_ERROR_UNKNOWN_DESTROY] =
"SolidSyslogPassthroughBuffer_Destroy called with a handle not issued by this pool",
[PASSTHROUGHBUFFER_ERROR_NULL_SENDER] =
"SolidSyslogPassthroughBuffer_Create called with NULL sender; returning fallback buffer",
};
const char* result = "unknown";
if (code < (uint8_t) PASSTHROUGHBUFFER_ERROR_MAX)
Expand Down
27 changes: 19 additions & 8 deletions Core/Source/SolidSyslogPassthroughBufferStatic.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,32 @@ static struct SolidSyslogPoolAllocator PassthroughBuffer_Allocator = {

struct SolidSyslogBuffer* SolidSyslogPassthroughBuffer_Create(struct SolidSyslogSender* sender)
{
size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&PassthroughBuffer_Allocator);
struct SolidSyslogBuffer* handle = SolidSyslogNullBuffer_Get();
if (SolidSyslogPoolAllocator_IndexIsValid(&PassthroughBuffer_Allocator, index))
{
PassthroughBuffer_Initialise(&PassthroughBuffer_Pool[index].Base, sender);
handle = &PassthroughBuffer_Pool[index].Base;
}
else
if (sender == NULL)
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&PassthroughBufferErrorSource,
(uint8_t) PASSTHROUGHBUFFER_ERROR_POOL_EXHAUSTED
(uint8_t) PASSTHROUGHBUFFER_ERROR_NULL_SENDER
);
}
else
{
size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&PassthroughBuffer_Allocator);
if (SolidSyslogPoolAllocator_IndexIsValid(&PassthroughBuffer_Allocator, index))
{
PassthroughBuffer_Initialise(&PassthroughBuffer_Pool[index].Base, sender);
handle = &PassthroughBuffer_Pool[index].Base;
}
else
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&PassthroughBufferErrorSource,
(uint8_t) PASSTHROUGHBUFFER_ERROR_POOL_EXHAUSTED
);
}
}
return handle;
}

Expand Down
63 changes: 54 additions & 9 deletions Tests/SolidSyslogPassthroughBufferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@

using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-file scope only; brings NEVER/ONCE/TWICE/THRICE into scope for the CALLED_*
// macros
#include "ErrorHandlerFake.h"
#include "SolidSyslogBuffer.h"
#include "SolidSyslogPassthroughBuffer.h"
#include "SolidSyslogPassthroughBufferErrors.h"
#include "SolidSyslogPrival.h"
#include "SolidSyslogTunables.h"
#include "SenderFake.h"

Expand Down Expand Up @@ -82,6 +85,18 @@ TEST(SolidSyslogPassthroughBuffer, ReadReturnsNothingToSend)
CHECK_FALSE(sent);
}

TEST(SolidSyslogPassthroughBuffer, DestroyWithNullHandleEmitsUnknownDestroyWarning)
{
ErrorHandlerFake_Install(nullptr);

SolidSyslogPassthroughBuffer_Destroy(nullptr);

CALLED_FAKE(ErrorHandlerFake_Handle, ONCE);
LONGS_EQUAL(SOLIDSYSLOG_SEVERITY_WARNING, ErrorHandlerFake_LastSeverity());
POINTERS_EQUAL(&PassthroughBufferErrorSource, ErrorHandlerFake_LastSource());
UNSIGNED_LONGS_EQUAL(PASSTHROUGHBUFFER_ERROR_UNKNOWN_DESTROY, ErrorHandlerFake_LastCode());
}

TEST(SolidSyslogPassthroughBuffer, UseAfterDestroyIsCrashSafeViaNullBufferVtable)
{
/* After Destroy the slot's abstract-base vtable is the shared NullBuffer's, so
Expand All @@ -99,15 +114,6 @@ TEST(SolidSyslogPassthroughBuffer, UseAfterDestroyIsCrashSafeViaNullBufferVtable
buffer = SolidSyslogPassthroughBuffer_Create(fakeSender); // for teardown
}

IGNORE_TEST(SolidSyslogPassthroughBuffer, HappyPathOnly)

{
// Error handling not yet implemented — see Epic #31
// Create with NULL sender returns NULL
// Write with NULL buffer does not crash
// Destroy with NULL buffer does not crash
}

// Pool tests — prove SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE caps live
// instances and overflow falls back to a distinct no-op buffer. Generic
// pool mechanics (lock counts, per-probe locking, stale-handle warning)
Expand Down Expand Up @@ -185,3 +191,42 @@ TEST(SolidSyslogPassthroughBufferPool, FallbackWriteAndReadAreNoOps)
LONGS_EQUAL(0, bytesRead);
CALLED_FAKE_ON(SenderFake_Send, fakeSender, NEVER);
}

TEST(SolidSyslogPassthroughBufferPool, CreateWithNullSenderReportsError)
{
ErrorHandlerFake_Install(nullptr);

overflow = SolidSyslogPassthroughBuffer_Create(nullptr);

CALLED_FAKE(ErrorHandlerFake_Handle, ONCE);
LONGS_EQUAL(SOLIDSYSLOG_SEVERITY_ERROR, ErrorHandlerFake_LastSeverity());
POINTERS_EQUAL(&PassthroughBufferErrorSource, ErrorHandlerFake_LastSource());
UNSIGNED_LONGS_EQUAL(PASSTHROUGHBUFFER_ERROR_NULL_SENDER, ErrorHandlerFake_LastCode());
}

TEST(SolidSyslogPassthroughBufferPool, CreateWithNullSenderReturnsFallbackDistinctFromAnyPoolSlot)
{
FillPool();

overflow = SolidSyslogPassthroughBuffer_Create(nullptr);

CHECK_TEXT(overflow != nullptr, "Fallback handle was nullptr");
for (auto* slot : pooled)
{
CHECK_TEXT(overflow != slot, "Fallback handle collided with a pool slot");
}
}

TEST(SolidSyslogPassthroughBufferPool, CreateWithNullSenderDoesNotConsumeAPoolSlot)
{
// If the failed Create had leaked its acquired slot, FillPool would overflow
// into the fallback one slot sooner and one pool slot would collide with
// `overflow` (both pointing at the NullBuffer singleton).
overflow = SolidSyslogPassthroughBuffer_Create(nullptr);

FillPool();
for (auto* slot : pooled)
{
CHECK_TEXT(slot != overflow, "Pool slot collided with the NULL-sender fallback handle");
}
}
Loading