184 lines
4.3 KiB
C
184 lines
4.3 KiB
C
#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]);
|
|
}
|
|
}
|