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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ EXTERN_C_BEGIN
{
POSIXMESSAGEQUEUEBUFFER_ERROR_POOL_EXHAUSTED,
POSIXMESSAGEQUEUEBUFFER_ERROR_UNKNOWN_DESTROY,
POSIXMESSAGEQUEUEBUFFER_ERROR_MQ_OPEN_FAILED,
POSIXMESSAGEQUEUEBUFFER_ERROR_SEND_FAILED,
POSIXMESSAGEQUEUEBUFFER_ERROR_RECEIVE_FAILED,
POSIXMESSAGEQUEUEBUFFER_ERROR_MAX
};

Expand Down
46 changes: 38 additions & 8 deletions Platform/Posix/Source/SolidSyslogPosixMessageQueueBuffer.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "SolidSyslogPosixMessageQueueBuffer.h"

#include <errno.h>
#include <fcntl.h>
#include <mqueue.h>
#include <stdbool.h>
Expand All @@ -8,10 +9,13 @@
#include <sys/types.h>

#include "SolidSyslogBufferDefinition.h"
#include "SolidSyslogError.h"
#include "SolidSyslogFormatter.h"
#include "SolidSyslogNullBuffer.h"
#include "SolidSyslogPosixMessageQueueBufferErrors.h"
#include "SolidSyslogPosixMessageQueueBufferPrivate.h"
#include "SolidSyslogPosixProcessId.h"
#include "SolidSyslogPrival.h"

enum
{
Expand All @@ -28,7 +32,7 @@ static inline struct SolidSyslogPosixMessageQueueBuffer* PosixMessageQueueBuffer
static inline const char* PosixMessageQueueBuffer_QueueName(struct SolidSyslogPosixMessageQueueBuffer* self);

// NOLINTBEGIN(bugprone-easily-swappable-parameters) -- distinct semantic meaning; mirrors the public _Create signature plus a per-slot discriminator
void PosixMessageQueueBuffer_Initialise(
bool PosixMessageQueueBuffer_Initialise(
struct SolidSyslogBuffer* base,
size_t maxMessageSize,
long maxMessages,
Expand Down Expand Up @@ -58,6 +62,7 @@ void PosixMessageQueueBuffer_Initialise(
self->MaxMessageSize = maxMessageSize;
self->Base.Write = PosixMessageQueueBuffer_Write;
self->Base.Read = PosixMessageQueueBuffer_Read;
return self->Mq != (mqd_t) -1;
}

void PosixMessageQueueBuffer_Cleanup(struct SolidSyslogBuffer* base)
Expand All @@ -77,19 +82,44 @@ static inline const char* PosixMessageQueueBuffer_QueueName(struct SolidSyslogPo

static bool PosixMessageQueueBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead)
{
struct SolidSyslogPosixMessageQueueBuffer* self = PosixMessageQueueBuffer_SelfFromBase(base);
ssize_t received = mq_receive(self->Mq, data, maxSize, NULL);
bool success = received >= 0;

*bytesRead = success ? (size_t) received : 0U;

bool success = false;
if (bytesRead != NULL)
{
struct SolidSyslogPosixMessageQueueBuffer* self = PosixMessageQueueBuffer_SelfFromBase(base);
ssize_t received = mq_receive(self->Mq, data, maxSize, NULL);
success = received >= 0;

/* Capture errno immediately after mq_receive so the EAGAIN test below
* stays a pure predicate and is decoupled from errno's lifetime
* between the errno-setting call and the read (MISRA C:2012 Rule 22.10).
* EAGAIN is the empty-queue poll signal — part of the happy path and
* must stay silent. Any other errno is a real failure worth surfacing. */
int receiveErrno = success ? 0 : errno;
if (!success && (receiveErrno != EAGAIN))
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&PosixMessageQueueBufferErrorSource,
(uint8_t) POSIXMESSAGEQUEUEBUFFER_ERROR_RECEIVE_FAILED
);
}

*bytesRead = success ? (size_t) received : 0U;
}
return success;
}

static void PosixMessageQueueBuffer_Write(struct SolidSyslogBuffer* base, const void* data, size_t size)
{
struct SolidSyslogPosixMessageQueueBuffer* self = PosixMessageQueueBuffer_SelfFromBase(base);
mq_send(self->Mq, data, size, 0);
if (mq_send(self->Mq, data, size, 0) != 0)
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&PosixMessageQueueBufferErrorSource,
(uint8_t) POSIXMESSAGEQUEUEBUFFER_ERROR_SEND_FAILED
);
}
}

static inline struct SolidSyslogPosixMessageQueueBuffer* PosixMessageQueueBuffer_SelfFromBase(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ static const char* PosixMessageQueueBufferError_AsString(uint8_t code)
"SolidSyslogPosixMessageQueueBuffer_Create pool exhausted; returning fallback buffer",
[POSIXMESSAGEQUEUEBUFFER_ERROR_UNKNOWN_DESTROY] =
"SolidSyslogPosixMessageQueueBuffer_Destroy called with a handle not issued by this pool",
[POSIXMESSAGEQUEUEBUFFER_ERROR_MQ_OPEN_FAILED] =
"SolidSyslogPosixMessageQueueBuffer_Create mq_open failed; returning fallback buffer",
[POSIXMESSAGEQUEUEBUFFER_ERROR_SEND_FAILED] =
"SolidSyslogPosixMessageQueueBuffer_Write mq_send failed; record dropped",
[POSIXMESSAGEQUEUEBUFFER_ERROR_RECEIVE_FAILED] = "SolidSyslogPosixMessageQueueBuffer_Read mq_receive failed",
};
const char* result = "unknown";
if (code < (uint8_t) POSIXMESSAGEQUEUEBUFFER_ERROR_MAX)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define SOLIDSYSLOGPOSIXMESSAGEQUEUEBUFFERPRIVATE_H

#include <mqueue.h>
#include <stdbool.h>
#include <stddef.h>

#include "SolidSyslogBufferDefinition.h"
Expand All @@ -21,7 +22,7 @@ struct SolidSyslogPosixMessageQueueBuffer
size_t MaxMessageSize;
};

void PosixMessageQueueBuffer_Initialise(
bool PosixMessageQueueBuffer_Initialise(
struct SolidSyslogBuffer* base,
size_t maxMessageSize,
long maxMessages,
Expand Down
21 changes: 19 additions & 2 deletions Platform/Posix/Source/SolidSyslogPosixMessageQueueBufferStatic.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,30 @@ struct SolidSyslogBuffer* SolidSyslogPosixMessageQueueBuffer_Create(size_t maxMe
struct SolidSyslogBuffer* handle = SolidSyslogNullBuffer_Get();
if (SolidSyslogPoolAllocator_IndexIsValid(&PosixMessageQueueBuffer_Allocator, index) == true)
{
PosixMessageQueueBuffer_Initialise(
bool opened = PosixMessageQueueBuffer_Initialise(
&PosixMessageQueueBuffer_Pool[index].Base,
maxMessageSize,
maxMessages,
index
);
handle = &PosixMessageQueueBuffer_Pool[index].Base;
if (opened)
{
handle = &PosixMessageQueueBuffer_Pool[index].Base;
}
else
{
(void) SolidSyslogPoolAllocator_FreeIfInUse(
&PosixMessageQueueBuffer_Allocator,
index,
PosixMessageQueueBuffer_CleanupAtIndex,
NULL
);
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&PosixMessageQueueBufferErrorSource,
(uint8_t) POSIXMESSAGEQUEUEBUFFER_ERROR_MQ_OPEN_FAILED
);
}
}
else
{
Expand Down
1 change: 1 addition & 0 deletions Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ if(SOLIDSYSLOG_POSIX)
SolidSyslogGetAddrInfoResolverTest.cpp
SolidSyslogPosixDatagramTest.cpp
SolidSyslogPosixTcpStreamTest.cpp
MqFakeTest.cpp
SocketFakeTest.cpp
)
endif()
Expand Down
166 changes: 166 additions & 0 deletions Tests/MqFakeTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <mqueue.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "MqFake.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 mqd_t OpenTestQueue(const char* name, long maxMessages = 4, size_t maxMessageSize = 64)
{
struct mq_attr attr = {0, maxMessages, static_cast<long>(maxMessageSize), 0, {0}};
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) -- POSIX mq_open is a varargs function when O_CREAT is set
return mq_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, &attr);
}

// clang-format off
TEST_GROUP(MqFake)
{
void setup() override { MqFake_Reset(); }
};
// clang-format on

TEST(MqFake, OpenIncrementsCount)
{
OpenTestQueue("/test");
CALLED_FAKE(MqFake_Open, ONCE);
}

TEST(MqFake, OpenCapturesName)
{
OpenTestQueue("/captured");
STRCMP_EQUAL("/captured", MqFake_LastOpenName());
}

TEST(MqFake, OpenCapturesAttrFields)
{
OpenTestQueue("/test", 7, 128);
LONGS_EQUAL(7, MqFake_LastOpenMaxMessages());
LONGS_EQUAL(128, MqFake_LastOpenMaxMessageSize());
}

TEST(MqFake, OpenReturnsDistinctMqdPerCall)
{
mqd_t first = OpenTestQueue("/first");
mqd_t second = OpenTestQueue("/second");
CHECK(first != second);
CHECK(first != (mqd_t) -1);
CHECK(second != (mqd_t) -1);
}

TEST(MqFake, FailNextOpenReturnsMinusOneAndSetsErrno)
{
MqFake_FailNextOpen(EINVAL);
mqd_t result = OpenTestQueue("/test");
LONGS_EQUAL(-1, (long) result);
LONGS_EQUAL(EINVAL, errno);
}

TEST(MqFake, FailNextOpenIsOneShot)
{
MqFake_FailNextOpen(EINVAL);
(void) OpenTestQueue("/first");
mqd_t second = OpenTestQueue("/second");
CHECK(second != (mqd_t) -1);
}

TEST(MqFake, OpenNameHistoryReturnsNameByIndex)
{
OpenTestQueue("/alpha");
OpenTestQueue("/beta");
STRCMP_EQUAL("/alpha", MqFake_OpenNameAt(0));
STRCMP_EQUAL("/beta", MqFake_OpenNameAt(1));
}

TEST(MqFake, SendStoresMessageForReceive)
{
mqd_t mqd = OpenTestQueue("/test");
LONGS_EQUAL(0, mq_send(mqd, "payload", 7, 0));

char buf[16] = {};
ssize_t received = mq_receive(mqd, buf, sizeof(buf), nullptr);
LONGS_EQUAL(7, received);
STRCMP_EQUAL("payload", buf);
}

TEST(MqFake, ReceiveFromEmptyQueueReturnsMinusOneEagain)
{
mqd_t mqd = OpenTestQueue("/test");

char buf[16] = {};
ssize_t result = mq_receive(mqd, buf, sizeof(buf), nullptr);
LONGS_EQUAL(-1, result);
LONGS_EQUAL(EAGAIN, errno);
}

TEST(MqFake, FailNextSendReturnsMinusOneAndSetsErrno)
{
mqd_t mqd = OpenTestQueue("/test");
MqFake_FailNextSend(EMSGSIZE);
LONGS_EQUAL(-1, mq_send(mqd, "x", 1, 0));
LONGS_EQUAL(EMSGSIZE, errno);
}

/* Natural overflow — sending past mq_attr.mq_maxmsg must surface as
* -1/EAGAIN per POSIX, not silently succeed. */
TEST(MqFake, SendBeyondMaxMessagesReturnsMinusOneEagain)
{
mqd_t mqd = OpenTestQueue("/test", /*maxMessages=*/2, /*maxMessageSize=*/64);
LONGS_EQUAL(0, mq_send(mqd, "a", 1, 0));
LONGS_EQUAL(0, mq_send(mqd, "b", 1, 0));

LONGS_EQUAL(-1, mq_send(mqd, "c", 1, 0));
LONGS_EQUAL(EAGAIN, errno);
}

/* Natural oversize — POSIX requires -1/EMSGSIZE, not truncation. */
TEST(MqFake, SendLargerThanMaxMessageSizeReturnsMinusOneEmsgsize)
{
mqd_t mqd = OpenTestQueue("/test", /*maxMessages=*/4, /*maxMessageSize=*/4);
LONGS_EQUAL(-1, mq_send(mqd, "fivefive", 8, 0));
LONGS_EQUAL(EMSGSIZE, errno);
}

TEST(MqFake, FailNextReceiveReturnsMinusOneAndSetsErrno)
{
mqd_t mqd = OpenTestQueue("/test");
mq_send(mqd, "x", 1, 0);
MqFake_FailNextReceive(EBADMSG);
char buf[16] = {};
LONGS_EQUAL(-1, mq_receive(mqd, buf, sizeof(buf), nullptr));
LONGS_EQUAL(EBADMSG, errno);
}

TEST(MqFake, CloseIncrementsCount)
{
mqd_t mqd = OpenTestQueue("/test");
mq_close(mqd);
CALLED_FAKE(MqFake_Close, ONCE);
LONGS_EQUAL((long) mqd, (long) MqFake_LastClosedMqd());
}

TEST(MqFake, UnlinkIncrementsCountAndCapturesName)
{
mq_unlink("/gone");
CALLED_FAKE(MqFake_Unlink, ONCE);
STRCMP_EQUAL("/gone", MqFake_LastUnlinkName());
}

TEST(MqFake, IsolatedQueuesDoNotShareMessages)
{
mqd_t alpha = OpenTestQueue("/alpha");
mqd_t beta = OpenTestQueue("/beta");

mq_send(alpha, "from-alpha", 10, 0);

char buf[16] = {};
ssize_t result = mq_receive(beta, buf, sizeof(buf), nullptr);
LONGS_EQUAL(-1, result);
LONGS_EQUAL(EAGAIN, errno);
}
Loading
Loading