test: cover timetest and waitanytest
This commit is contained in:
1 parent
d4f2912238
commit
24803ae957
2 files changed
+191
No files matched your search
@@ -0,0 +1,89 @@
|
||||
#include "ulib.h"
|
||||
#include "fs.h"
|
||||
|
||||
/*
|
||||
* Exercises SFS's new real wall-clock inode timestamps (sfs.h's
|
||||
* sfsMtimeOf/sfsCtimeOf) through the exact same syscall path a real
|
||||
* userspace program would use (userOpen/userWrite/userStat/userLink on
|
||||
* "/mnt", like fstest.c/linktest.c already do for size/content), proving the
|
||||
* full stack -- syscall -> vfs -> sfs -> the on-disk inode's mtime/ctime
|
||||
* fields, stamped from the real CMOS RTC (rtc.c) -- works end to end, not
|
||||
* just the in-kernel sfs.c unit tests. Fully synchronous like nicetest.c/
|
||||
* date.c: no injected keystrokes, no shared shell channel, so there is no
|
||||
* scheduling-timing race to flake on regardless of host load.
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("timetest: start, pid=");
|
||||
printDec(userGetpid());
|
||||
print("\n");
|
||||
|
||||
if (userMkdir("/mnt/timetest") != 0) {
|
||||
print("timetest: FAIL mkdir /mnt/timetest\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
long before = userTime();
|
||||
int fd = (int)userOpen("/mnt/timetest/data.txt", oReadWrite | oCreate);
|
||||
long after = userTime();
|
||||
if (fd < 0) {
|
||||
print("timetest: FAIL open for create\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FbStat st;
|
||||
if (userStat("/mnt/timetest/data.txt", &st) != 0) {
|
||||
print("timetest: FAIL stat after create\n");
|
||||
return 1;
|
||||
}
|
||||
long createdMtime = (long)st.mtime;
|
||||
long createdCtime = (long)st.ctime;
|
||||
if (createdMtime != createdCtime || createdMtime < before || createdMtime > after) {
|
||||
print("timetest: FAIL creation timestamp out of range\n");
|
||||
return 1;
|
||||
}
|
||||
print("timetest: create-stamped-ok\n");
|
||||
|
||||
const char* payload = "timetest payload\n";
|
||||
unsigned long payloadLen = stringLength(payload);
|
||||
if (userWrite(fd, payload, payloadLen) != (long)payloadLen) {
|
||||
print("timetest: FAIL write\n");
|
||||
return 1;
|
||||
}
|
||||
userClose(fd);
|
||||
|
||||
if (userStat("/mnt/timetest/data.txt", &st) != 0) {
|
||||
print("timetest: FAIL stat after write\n");
|
||||
return 1;
|
||||
}
|
||||
if ((long)st.mtime != (long)st.ctime || (long)st.mtime < createdMtime) {
|
||||
print("timetest: FAIL write did not bump mtime\n");
|
||||
return 1;
|
||||
}
|
||||
print("timetest: write-stamped-ok\n");
|
||||
long mtimeAfterWrite = (long)st.mtime;
|
||||
|
||||
if (userLink("/mnt/timetest/data.txt", "/mnt/timetest/alias.txt") != 0) {
|
||||
print("timetest: FAIL hardlink\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FbStat aliasSt;
|
||||
if (userStat("/mnt/timetest/alias.txt", &aliasSt) != 0) {
|
||||
print("timetest: FAIL stat alias\n");
|
||||
return 1;
|
||||
}
|
||||
if ((long)aliasSt.mtime != mtimeAfterWrite) {
|
||||
print("timetest: FAIL hardlink does not share mtime\n");
|
||||
return 1;
|
||||
}
|
||||
if ((long)aliasSt.ctime < mtimeAfterWrite) {
|
||||
print("timetest: FAIL hardlink ctime went backwards\n");
|
||||
return 1;
|
||||
}
|
||||
print("timetest: hardlink-shares-mtime-ok\n");
|
||||
|
||||
print("timetest: PASS\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Boot-time driver for waitpid(-1)-style userWaitAny (ulib.c/sched.c's new
|
||||
* threadJoinAnyChild) -- proves a parent can reap TWO independently exiting
|
||||
* children without ever naming either pid, in whichever order they
|
||||
* actually die, and correctly reports ECHILD (-1, no blocking) once none
|
||||
* are left.
|
||||
*
|
||||
* Spawns waitanyfast.c (exits immediately) then waitanyslow.c (sleeps
|
||||
* 150ms first), then calls userWaitAny() twice:
|
||||
* - the first call happens essentially immediately after both spawns, so
|
||||
* waitanyslow.c cannot possibly be Dead yet (its own 150ms sleep alone
|
||||
* dwarfs the handful of syscalls in between) -- it must be
|
||||
* waitanyfast.c that gets reaped here, exercising
|
||||
* threadJoinAnyChild's waitScanFoundDead branch without ever blocking.
|
||||
* - the second call therefore has only waitanyslow.c left, which is
|
||||
* genuinely still alive at that point -- it must take the
|
||||
* waitScanNoneDead branch and actually block through sysWait's real
|
||||
* retry-syscall path (schedBlockCurrent(currentThread()), sched.c's
|
||||
* new any-child wake in threadExit) until waitanyslow.c's sleep
|
||||
* elapses and it exits.
|
||||
* Verified without leaning on that ordering being guaranteed, though: both
|
||||
* reaped pids together must equal exactly {fastPid, slowPid} as a set, and
|
||||
* each one's own exit code must match whichever pid it actually was, in
|
||||
* whatever order they came back.
|
||||
* A third userWaitAny() call, once both children are gone, must return -1
|
||||
* immediately (ECHILD) rather than hang forever with no children left to
|
||||
* wait for -- proves threadJoinAnyChild's waitScanNoChildren branch live.
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("waitanytest: start, pid=");
|
||||
printDec(userGetpid());
|
||||
print("\n");
|
||||
|
||||
long fastPid = userSpawn("/bin/waitanyfast", 0);
|
||||
if (fastPid < 0) {
|
||||
print("waitanytest: FAIL spawn waitanyfast\n");
|
||||
return 1;
|
||||
}
|
||||
long slowPid = userSpawn("/bin/waitanyslow", 0);
|
||||
if (slowPid < 0) {
|
||||
print("waitanytest: FAIL spawn waitanyslow\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
long reapedPid[2];
|
||||
long reapedCode[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
long pid = -1;
|
||||
long code = userWaitAny(&pid);
|
||||
print("waitanytest: reaped pid=");
|
||||
printDec(pid);
|
||||
print(" code=");
|
||||
printDec(code);
|
||||
print("\n");
|
||||
if (code < 0) {
|
||||
print("waitanytest: FAIL userWaitAny reported no children too early\n");
|
||||
return 1;
|
||||
}
|
||||
reapedPid[i] = pid;
|
||||
reapedCode[i] = code;
|
||||
}
|
||||
|
||||
int sawFast = 0;
|
||||
int sawSlow = 0;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (reapedPid[i] == fastPid) {
|
||||
sawFast = 1;
|
||||
if (reapedCode[i] != 44) {
|
||||
print("waitanytest: FAIL waitanyfast exit code mismatch\n");
|
||||
return 1;
|
||||
}
|
||||
} else if (reapedPid[i] == slowPid) {
|
||||
sawSlow = 1;
|
||||
if (reapedCode[i] != 33) {
|
||||
print("waitanytest: FAIL waitanyslow exit code mismatch\n");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
print("waitanytest: FAIL reaped an unrecognized pid\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!sawFast || !sawSlow) {
|
||||
print("waitanytest: FAIL did not reap both known children exactly once\n");
|
||||
return 1;
|
||||
}
|
||||
print("waitanytest: any-child-reap-set-ok\n");
|
||||
|
||||
long pid = 0;
|
||||
long status = userWaitAny(&pid);
|
||||
if (status != -1) {
|
||||
print("waitanytest: FAIL expected -1 (ECHILD) once no children remain\n");
|
||||
return 1;
|
||||
}
|
||||
print("waitanytest: echild-when-no-children-ok\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user