From 2dd54aa236f28ed221f38ca3d0ad6d58c3d69d54 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sat, 12 Sep 2026 19:42:30 +0200 Subject: [PATCH] test: cover bytesEqual --- user/fstest.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 user/fstest.c diff --git a/user/fstest.c b/user/fstest.c new file mode 100644 index 0000000..78059f0 --- /dev/null +++ b/user/fstest.c @@ -0,0 +1,150 @@ +#include "ulib.h" +#include "fs.h" + +/* + * Exercises the persistent SFS filesystem (sfs.h/sfs.c) through the exact + * same syscall path a real userspace program would use: open/write/read/ + * close/mkdir/getdents/unlink on paths under "/mnt", which vfs.c dispatches + * to sfs.c instead of the kmalloc-based tmpfs tree. Proves the full stack + * (syscall -> vfs -> sfs -> real block I/O on the same ATA disk /dev/disk0 + * uses) works end to end, not just the in-kernel sfs.c unit tests. + */ + +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("fstest: start, pid="); + printDec(userGetpid()); + print("\n"); + + /* Nested directory creation. */ + if (userMkdir("/mnt/app") != 0) { + print("fstest: FAIL mkdir /mnt/app\n"); + return 1; + } + if (userMkdir("/mnt/app/logs") != 0) { + print("fstest: FAIL mkdir /mnt/app/logs\n"); + return 1; + } + + /* Create + write + close, then reopen with a fresh fd to prove the data + lives past the writer's own fd, not just in that fd's local state. */ + const char* payload = "frostbyte fstest payload line one\n"; + unsigned long payloadLen = stringLength(payload); + + int wfd = (int)userOpen("/mnt/app/logs/boot.log", oReadWrite | oCreate); + if (wfd < 0) { + print("fstest: FAIL open for create\n"); + return 1; + } + if (userWrite(wfd, payload, payloadLen) != (long)payloadLen) { + print("fstest: FAIL short write\n"); + return 1; + } + userClose(wfd); + + FbStat st; + if (userStat("/mnt/app/logs/boot.log", &st) != 0 || st.size != payloadLen) { + print("fstest: FAIL stat size mismatch\n"); + return 1; + } + + int rfd = (int)userOpen("/mnt/app/logs/boot.log", oReadOnly); + if (rfd < 0) { + print("fstest: FAIL reopen\n"); + return 1; + } + char back[128]; + long got = userRead(rfd, back, sizeof(back)); + if (got != (long)payloadLen || !bytesEqual(back, payload, payloadLen)) { + print("fstest: FAIL reopen round-trip mismatch\n"); + return 1; + } + userClose(rfd); + print("fstest: create/write/reopen/read round-trip ok\n"); + + /* Append across two separate open() calls: growth must pick up where + the previous session's data ended. */ + int afd = (int)userOpen("/mnt/app/logs/boot.log", oReadWrite | oAppend); + if (afd < 0) { + print("fstest: FAIL open for append\n"); + return 1; + } + const char* more = "line two\n"; + unsigned long moreLen = stringLength(more); + if (userWrite(afd, more, moreLen) != (long)moreLen) { + print("fstest: FAIL append write\n"); + return 1; + } + userClose(afd); + + if (userStat("/mnt/app/logs/boot.log", &st) != 0 || st.size != payloadLen + moreLen) { + print("fstest: FAIL stat after append\n"); + return 1; + } + print("fstest: append across reopen ok\n"); + + /* A second, unrelated file in the same directory, to exercise readdir + against more than one entry. */ + int cfd = (int)userOpen("/mnt/app/logs/counters.txt", oReadWrite | oCreate); + if (cfd < 0) { + print("fstest: FAIL create second file\n"); + return 1; + } + userWrite(cfd, "0", 1); + userClose(cfd); + + int dfd = (int)userOpen("/mnt/app/logs", oReadOnly); + if (dfd < 0) { + print("fstest: FAIL open dir for listing\n"); + return 1; + } + FbDirent entries[8]; + long entCount = userGetdents(dfd, entries, sizeof(entries)); + userClose(dfd); + if (entCount != 2 * (long)sizeof(FbDirent)) { + print("fstest: FAIL readdir entry count\n"); + return 1; + } + int sawBoot = 0, sawCounters = 0; + for (int i = 0; i < 2; i++) { + if (entries[i].type != fsTypeFile) { + print("fstest: FAIL readdir entry type\n"); + return 1; + } + if (bytesEqual(entries[i].name, "boot.log", 9)) + sawBoot = 1; + if (bytesEqual(entries[i].name, "counters.txt", 13)) + sawCounters = 1; + } + if (!sawBoot || !sawCounters) { + print("fstest: FAIL readdir missing an expected entry\n"); + return 1; + } + print("fstest: readdir lists both files ok\n"); + + /* Unlink one, verify it is gone and its sibling survives. */ + if (userUnlink("/mnt/app/logs/counters.txt") != 0) { + print("fstest: FAIL unlink counters.txt\n"); + return 1; + } + if (userStat("/mnt/app/logs/counters.txt", &st) == 0) { + print("fstest: FAIL unlinked file still stat-able\n"); + return 1; + } + if (userStat("/mnt/app/logs/boot.log", &st) != 0) { + print("fstest: FAIL sibling disappeared after unlink\n"); + return 1; + } + print("fstest: unlink ok, sibling survives\n"); + + print("fstest: done\n"); + return 0; +}