test: cover pollTest

This commit is contained in:
dualfroz committed 2026-09-10 20:59:27 +02:00
1 parent 9dfdd34540
commit bac6a25d5d
1 file changed
+146
+146
View File
@@ -0,0 +1,146 @@
#include "ktest.h"
#include "poll.h"
#include "vfs.h"
#include "pipe.h"
/*
* Pure, scheduler-free coverage for vfsPollReady (vfs.c) and its two
* non-consuming peek helpers (pipeHasData/pipeHasSpace, pipe.h;
* keyboardReaderHasData, keyboard.h) -- everything that can be driven
* single-threaded, exactly like pipeShareTest.c/vfsFifoTest.c already do
* for the machinery poll.h's sysPoll is built on top of. The genuine
* cross-process block-then-wake handshake (schedule()'s own pollDeadline
* expiry scan, sched.c; the shared schedPollChannel wake from a SECOND,
* independently scheduled writer) only runs for real at boot
* (user/polltest.c + user/pollwriter.c, needles in tools/ci-test.sh) --
* the same division of labor this file's neighbors already use, and
* schedTest.c's own doc comment explains why: ktestRunAll() runs before
* schedStart(), with no second execution context to switch into.
*/
KTEST(vfsPollReadyReportsNvalForClosedOrOutOfRangeFd)
{
int16 revents = 99;
KT_TRUE(t, vfsPollReady(-1, pollIn, &revents));
KT_EQ(t, revents, pollNval);
revents = 99;
KT_TRUE(t, vfsPollReady(9999, pollIn | pollOut, &revents));
KT_EQ(t, revents, pollNval);
}
KTEST(vfsPollReadyPipeReadEndTracksRealData)
{
int readFd, writeFd;
KT_EQ(t, vfsPipe(&readFd, &writeFd), 0);
int16 revents = 99;
KT_FALSE(t, vfsPollReady(readFd, pollIn, &revents));
KT_EQ(t, revents, 0);
KT_EQ(t, vfsWrite(writeFd, "hi", 2), 2);
revents = 0;
KT_TRUE(t, vfsPollReady(readFd, pollIn, &revents));
KT_EQ(t, revents, pollIn);
/* Asking for pollOut on a read-only end is never satisfiable, no
matter how much data is queued. */
revents = 99;
KT_FALSE(t, vfsPollReady(readFd, pollOut, &revents));
KT_EQ(t, revents, 0);
char buffer[8];
KT_EQ(t, vfsRead(readFd, buffer, sizeof(buffer)), 2);
vfsClose(readFd);
vfsClose(writeFd);
}
KTEST(vfsPollReadyPipeWriteEndTracksFreeSpaceThenFull)
{
int readFd, writeFd;
KT_EQ(t, vfsPipe(&readFd, &writeFd), 0);
int16 revents = 0;
KT_TRUE(t, vfsPollReady(writeFd, pollOut, &revents));
KT_EQ(t, revents, pollOut);
/* Fill the ring completely (pipeBufferSize, pipe.h) with non-blocking
writes -- vfsPipe's ends are exactly the unshared, "-1 means try
again" kind pipetest.c already depends on, so this can never park
the calling (only) thread the way a fdFlagBlocking end would. */
char chunk[64];
for (int i = 0; i < 64; i++)
chunk[i] = (char)i;
int64 written = 0;
for (int guard = 0; guard < 16; guard++) {
int64 got = vfsWrite(writeFd, chunk, sizeof(chunk));
if (got <= 0)
break;
written += got;
}
KT_TRUE(t, written > 0);
KT_EQ(t, vfsWrite(writeFd, chunk, sizeof(chunk)), -1);
revents = 99;
KT_FALSE(t, vfsPollReady(writeFd, pollOut, &revents));
KT_EQ(t, revents, 0);
/* Draining even a little frees space back up. */
char sink[16];
KT_EQ(t, vfsRead(readFd, sink, sizeof(sink)), 16);
revents = 0;
KT_TRUE(t, vfsPollReady(writeFd, pollOut, &revents));
KT_EQ(t, revents, pollOut);
vfsClose(readFd);
vfsClose(writeFd);
}
KTEST(vfsPollReadyReadEndReportsEofAfterWriterCloses)
{
int readFd, writeFd;
KT_EQ(t, vfsPipe(&readFd, &writeFd), 0);
int16 revents = 0;
KT_FALSE(t, vfsPollReady(readFd, pollIn, &revents));
vfsClose(writeFd);
/* A true EOF pipe is "readable" in real poll(2) too -- the next read
call returning 0 immediately is exactly what pollIn promises here. */
revents = 0;
KT_TRUE(t, vfsPollReady(readFd, pollIn, &revents));
KT_EQ(t, revents, pollIn);
char probe[4];
KT_EQ(t, vfsRead(readFd, probe, sizeof(probe)), 0);
vfsClose(readFd);
}
KTEST(vfsPollReadyConsoleWriteAlwaysReadyReadNeverWithoutInput)
{
/* fd 0 (console) is already live for the current (idle) thread by the
time ktestRunAll() runs -- schedInit() calls vfsFdInit before
kmain.c ever reaches ktestRunAll(). Every open reader (idle's fd 0
included) gets a copy of every keystroke keyboardHandleScancode ever
broadcasts (keyboard.h), including the synthetic ones OTHER test
files inject (keyboardTest.c's own coverage) -- KTEST execution
order across files is whatever the ".ktests" linker section happens
to lay them out in, so idle's fd 0 must be drained here rather than
assumed empty, or this test's own result would depend on that order. */
char drain[16];
while (vfsRead(0, drain, sizeof(drain)) > 0) { }
int16 revents = 99;
KT_FALSE(t, vfsPollReady(0, pollIn, &revents));
KT_EQ(t, revents, 0);
revents = 0;
KT_TRUE(t, vfsPollReady(0, pollOut, &revents));
KT_EQ(t, revents, pollOut);
revents = 0;
KT_TRUE(t, vfsPollReady(0, pollIn | pollOut, &revents));
KT_EQ(t, revents, pollOut);
}