From 9dfdd34540bcfc276bccb6dee9b44f84afc57f7c Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 10 Sep 2026 20:16:16 +0200 Subject: [PATCH] test: cover pipeTest --- src/pipeTest.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 src/pipeTest.c diff --git a/src/pipeTest.c b/src/pipeTest.c new file mode 100644 index 0000000..ae5803e --- /dev/null +++ b/src/pipeTest.c @@ -0,0 +1,183 @@ +#include "ktest.h" +#include "pipe.h" +#include "string.h" + +KTEST(pipeWriteThenReadRoundTrips) +{ + int h = pipeCreate(); + KT_GE(t, h, 0); + + const char* msg = "hello pipe"; + int64 written = pipeWrite(h, msg, strlen(msg)); + KT_EQ(t, written, (int64)strlen(msg)); + + char buffer[32]; + memset(buffer, 0, sizeof(buffer)); + int64 got = pipeRead(h, buffer, sizeof(buffer)); + KT_EQ(t, got, (int64)strlen(msg)); + KT_TRUE(t, memcmp(buffer, msg, strlen(msg)) == 0); + + pipeCloseRead(h); + pipeCloseWrite(h); +} + +KTEST(pipeReadOnEmptyOpenPipeWouldBlock) +{ + int h = pipeCreate(); + KT_GE(t, h, 0); + + char buffer[8]; + KT_EQ(t, pipeRead(h, buffer, sizeof(buffer)), -1); + + pipeCloseRead(h); + pipeCloseWrite(h); +} + +KTEST(pipeReadZeroLengthIsNoop) +{ + int h = pipeCreate(); + KT_GE(t, h, 0); + + char buffer[1]; + KT_EQ(t, pipeRead(h, buffer, 0), 0); + KT_EQ(t, pipeWrite(h, buffer, 0), 0); + + pipeCloseRead(h); + pipeCloseWrite(h); +} + +KTEST(pipeFullWriteWouldBlockThenDrainsAndAcceptsMore) +{ + int h = pipeCreate(); + KT_GE(t, h, 0); + + uint8 chunk[pipeBufferSize]; + memset(chunk, 0xAB, sizeof(chunk)); + + /* this exactly fills the ring. */ + int64 filled = pipeWrite(h, chunk, sizeof(chunk)); + KT_EQ(t, filled, (int64)sizeof(chunk)); + + /* buffer is full: further write must report "would block" */ + KT_EQ(t, pipeWrite(h, chunk, 1), -1); + + /* drain some bytes, then more space is available */ + uint8 drained[64]; + int64 gotDrain = pipeRead(h, drained, sizeof(drained)); + KT_EQ(t, gotDrain, (int64)sizeof(drained)); + for (uint64 i = 0; i < sizeof(drained); i++) + KT_EQU(t, drained[i], 0xAB); + + int64 moreWritten = pipeWrite(h, chunk, sizeof(drained)); + KT_EQ(t, moreWritten, (int64)sizeof(drained)); + + pipeCloseRead(h); + pipeCloseWrite(h); +} + +KTEST(pipeEofAfterWriterClosedAndBufferDrained) +{ + int h = pipeCreate(); + KT_GE(t, h, 0); + + const char* msg = "last bytes"; + KT_EQ(t, pipeWrite(h, msg, strlen(msg)), (int64)strlen(msg)); + + pipeCloseWrite(h); + + /* data queued before close is still readable */ + char buffer[32]; + int64 got = pipeRead(h, buffer, sizeof(buffer)); + KT_EQ(t, got, (int64)strlen(msg)); + + /* now drained and no writer left: true end-of-file, not "would block" */ + KT_EQ(t, pipeRead(h, buffer, sizeof(buffer)), 0); + KT_EQ(t, pipeRead(h, buffer, sizeof(buffer)), 0); + + pipeCloseRead(h); +} + +KTEST(pipeWriteAfterReaderClosedIsBrokenPipe) +{ + int h = pipeCreate(); + KT_GE(t, h, 0); + + pipeCloseRead(h); + + const char* msg = "nobody listening"; + KT_EQ(t, pipeWrite(h, msg, strlen(msg)), -1); + + pipeCloseWrite(h); +} + +KTEST(pipeInvalidHandleIsRejected) +{ + KT_FALSE(t, pipeValid(-1)); + KT_FALSE(t, pipeValid(999)); + + char buffer[4]; + KT_EQ(t, pipeRead(-1, buffer, sizeof(buffer)), -1); + KT_EQ(t, pipeWrite(-1, buffer, sizeof(buffer)), -1); + + /* closing an invalid handle must not crash */ + pipeCloseRead(-1); + pipeCloseWrite(999); +} + +KTEST(pipeIndependentPipesDoNotInterfere) +{ + int a = pipeCreate(); + int b = pipeCreate(); + KT_GE(t, a, 0); + KT_GE(t, b, 0); + KT_NE(t, a, b); + + KT_EQ(t, pipeWrite(a, "AAAA", 4), 4); + KT_EQ(t, pipeWrite(b, "BB", 2), 2); + + char bufA[8]; + char bufB[8]; + memset(bufA, 0, sizeof(bufA)); + memset(bufB, 0, sizeof(bufB)); + + KT_EQ(t, pipeRead(b, bufB, sizeof(bufB)), 2); + KT_TRUE(t, memcmp(bufB, "BB", 2) == 0); + + KT_EQ(t, pipeRead(a, bufA, sizeof(bufA)), 4); + KT_TRUE(t, memcmp(bufA, "AAAA", 4) == 0); + + pipeCloseRead(a); + pipeCloseWrite(a); + pipeCloseRead(b); + pipeCloseWrite(b); +} + +KTEST(pipeTableExhaustionAndReuse) +{ + int handles[maxPipes + 1]; + int created = 0; + + while (created < maxPipes + 1) { + int h = pipeCreate(); + if (h < 0) + break; + handles[created++] = h; + } + + /* the table must run out before maxPipes+1 succeed */ + KT_LT(t, created, maxPipes + 1); + KT_EQ(t, pipeCreate(), -1); + + /* fully closing one pipe frees its slot for reuse */ + pipeCloseRead(handles[0]); + pipeCloseWrite(handles[0]); + int reused = pipeCreate(); + KT_GE(t, reused, 0); + + pipeCloseRead(reused); + pipeCloseWrite(reused); + for (int i = 1; i < created; i++) { + pipeCloseRead(handles[i]); + pipeCloseWrite(handles[i]); + } +}