From b4fbcaf66076a30365e4403b16e9417e7a6ae5d2 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sat, 12 Sep 2026 19:38:11 +0200 Subject: [PATCH] test: cover printLong and bytesEqual --- user/disktest.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++ user/eintrtest.c | 71 +++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 user/disktest.c create mode 100644 user/eintrtest.c diff --git a/user/disktest.c b/user/disktest.c new file mode 100644 index 0000000..ff8cfdc --- /dev/null +++ b/user/disktest.c @@ -0,0 +1,131 @@ +#include "ulib.h" +#include "fs.h" + +static const char* diskPath = "/dev/disk0"; + +static void printLong(const char* label, long value) +{ + print(label); + printDec(value); + print("\n"); +} + +static int bytesEqual(const char* a, const char* b, unsigned long n) +{ + for (unsigned long i = 0; i < n; i++) + if (a[i] != b[i]) + return 0; + return 1; +} + +int main(void) +{ + print("disktest: start, pid="); + printDec(userGetpid()); + print("\n"); + + FbStat st; + if (userStat(diskPath, &st) != 0) { + print("disktest: stat failed\n"); + return 1; + } + printLong("disktest: disk size bytes=", (long)st.size); + if (st.size < 64 * 1024) { + print("disktest: FAIL disk suspiciously small\n"); + return 1; + } + + int fd = (int)userOpen(diskPath, oReadWrite); + if (fd < 0) { + print("disktest: open failed\n"); + return 1; + } + + long end = userLseek(fd, 0, seekEnd); + printLong("disktest: seekEnd=", end); + if (end != (long)st.size) { + print("disktest: FAIL seekEnd does not match stat size\n"); + return 1; + } + + /* Unaligned write/read spanning a 512-byte sector boundary: byte offset + 50*512 + 500 leaves 12 bytes in sector 50 and spills 8 bytes into + sector 51, exercising the read-modify-write path in vfs.c blockDevRead + /blockDevWrite rather than a single aligned sector transfer. */ + const long straddleOffset = 50L * 512 + 500; + const char* straddlePayload = "ABCDEFGHIJKLMNOPQRST"; /* 20 bytes */ + const unsigned long straddleLen = 20; + + if (userLseek(fd, straddleOffset, seekSet) != straddleOffset) { + print("disktest: FAIL lseek to straddle offset\n"); + return 1; + } + long written = userWrite(fd, straddlePayload, straddleLen); + printLong("disktest: straddle write bytes=", written); + if (written != (long)straddleLen) { + print("disktest: FAIL short straddle write\n"); + return 1; + } + + if (userLseek(fd, straddleOffset, seekSet) != straddleOffset) { + print("disktest: FAIL lseek back to straddle offset\n"); + return 1; + } + char straddleBack[20]; + long straddleRead = userRead(fd, straddleBack, straddleLen); + if (straddleRead != (long)straddleLen || !bytesEqual(straddleBack, straddlePayload, straddleLen)) { + print("disktest: FAIL straddle round-trip mismatch\n"); + return 1; + } + print("disktest: straddle round-trip ok\n"); + + /* Aligned full-sector write/read at a disjoint offset (sector 55), + proving the non-read-modify-write path also works. */ + const long alignedOffset = 55L * 512; + char alignedOut[512]; + for (int i = 0; i < 512; i++) + alignedOut[i] = (char)('a' + (i % 26)); + + if (userLseek(fd, alignedOffset, seekSet) != alignedOffset) { + print("disktest: FAIL lseek to aligned offset\n"); + return 1; + } + if (userWrite(fd, alignedOut, sizeof(alignedOut)) != (long)sizeof(alignedOut)) { + print("disktest: FAIL aligned sector write\n"); + return 1; + } + if (userLseek(fd, alignedOffset, seekSet) != alignedOffset) { + print("disktest: FAIL lseek back to aligned offset\n"); + return 1; + } + char alignedIn[512]; + if (userRead(fd, alignedIn, sizeof(alignedIn)) != (long)sizeof(alignedIn) || + !bytesEqual(alignedIn, alignedOut, sizeof(alignedOut))) { + print("disktest: FAIL aligned sector round-trip mismatch\n"); + return 1; + } + print("disktest: aligned sector round-trip ok\n"); + + /* Overwrite proves the second write actually lands on disk, not just in + a stale in-memory copy: reading back must see the newest data. */ + if (userLseek(fd, alignedOffset, seekSet) != alignedOffset) + return 1; + char overwrite[512]; + for (int i = 0; i < 512; i++) + overwrite[i] = (char)('A' + (i % 26)); + userWrite(fd, overwrite, sizeof(overwrite)); + + if (userLseek(fd, alignedOffset, seekSet) != alignedOffset) + return 1; + char reread[512]; + userRead(fd, reread, sizeof(reread)); + if (!bytesEqual(reread, overwrite, sizeof(overwrite)) || bytesEqual(reread, alignedOut, sizeof(alignedOut))) { + print("disktest: FAIL overwrite not reflected\n"); + return 1; + } + print("disktest: overwrite round-trip ok\n"); + + userClose(fd); + print("disktest: done\n"); + return 0; +} diff --git a/user/eintrtest.c b/user/eintrtest.c new file mode 100644 index 0000000..791a946 --- /dev/null +++ b/user/eintrtest.c @@ -0,0 +1,71 @@ +#include "ulib.h" + +/* + * Boot-time driver for early-wake signal delivery (signal.h/sched.c's + * threadWakeForSignal): spawns eintrtarget.c, waits for a real + * happens-before confirmation that it is about to block forever on a read + * that no writer will ever satisfy (the same unsatisfiable-by-construction + * setup killtest.c's killvictim.c already established for threadKill), and + * sends it a real sigTerm through userSigSend -- an ORDINARY signal with + * its real default disposition (Terminate), not threadKill's direct + * bypass. + * + * If userWait below ever hung the whole boot (see ci-test.sh's 25s QEMU + * timeout), that would itself BE the regression this proves against: a + * Blocked thread that never naturally wakes has no other way back to ring 3 + * for isr.c's deliverPendingSignals to ever reach it, without the + * early-wake this test exists to exercise. + */ + +int main(void) +{ + print("eintrtest: start, pid="); + printDec(userGetpid()); + print("\n"); + + long readyHandle = userPipeCreate(); + if (readyHandle < 0) { + print("eintrtest: FAIL create ready pipe\n"); + return 1; + } + long readyFd = userPipeAttach(readyHandle, 0); + if (readyFd < 0) { + print("eintrtest: FAIL attach ready pipe\n"); + return 1; + } + + char readyHandleStr[24]; + decToString(readyHandle, readyHandleStr); + char* targetArgv[] = { "/bin/eintrtarget", readyHandleStr, 0 }; + long target = userSpawn("/bin/eintrtarget", targetArgv); + if (target < 0) { + print("eintrtest: FAIL spawn eintrtarget\n"); + return 1; + } + + char readyByte; + if (userRead((int)readyFd, &readyByte, 1) != 1) { + print("eintrtest: FAIL read ready byte\n"); + return 1; + } + userClose((int)readyFd); + print("eintrtest: eintrtarget ready\n"); + + if (userSigSend(target, sigTerm) != 0) { + print("eintrtest: FAIL send sigTerm to eintrtarget\n"); + return 1; + } + print("eintrtest: sent sigTerm to eintrtarget\n"); + + long status = userWait(target); + print("eintrtest: eintrtarget exit status="); + printDec(status); + print("\n"); + if (status != 128 + sigTerm) { + print("eintrtest: FAIL expected eintrtarget exit status 128+sigTerm\n"); + return 1; + } + print("eintrtest: early-wake-signal-ok\n"); + + return 0; +}