diff options
Diffstat (limited to 'kernel/test')
-rw-r--r-- | kernel/test/proctest.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/kernel/test/proctest.c b/kernel/test/proctest.c index 31067cd..e8b8ba8 100644 --- a/kernel/test/proctest.c +++ b/kernel/test/proctest.c @@ -21,6 +21,7 @@ void *test_func(long arg1, void *arg2) test_assert(arg1 == proc_as_arg->p_pid, "Arguments are not set up correctly"); test_assert(proc_as_arg->p_state == PROC_RUNNING, "Process state is not running"); test_assert(list_empty(&proc_as_arg->p_children), "There should be no child processes"); + dbg(DBG_TEST, "Process %s is running\n", proc_as_arg->p_name); return NULL; } @@ -36,6 +37,7 @@ void test_termination() int status; while (do_waitpid(-1, &status, 0) != -ECHILD) { + dbg(DBG_TEST, "Waiting for child process to terminate\n"); test_assert(status == 0, "Returned status not set correctly"); count++; } @@ -43,6 +45,265 @@ void test_termination() "Expected: %d, Actual: %d number of processes have been cleaned up\n", num_procs_created, count); } +/* + Tests the edge cases of do_waitpid. + 1. No child processes to wait for + 2. Waiting on a specific process id + 3. Waiting any child process (-1) +*/ +void test_do_waitpid() +{ + int status; + int ret = do_waitpid(-1, &status, 0); + test_assert(ret == -ECHILD, "No child processes to wait for"); + + proc_t *new_proc1 = proc_create("proc test 1"); + kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1); + sched_make_runnable(new_kthread1); + + proc_t *new_proc2 = proc_create("proc test 2"); + kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2); + sched_make_runnable(new_kthread2); + + proc_t *new_proc3 = proc_create("proc test 3"); + kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3); + sched_make_runnable(new_kthread3); + + // wait for a specific process id + ret = do_waitpid(new_proc2->p_pid, &status, 0); + test_assert(ret == new_proc2->p_pid, "Waiting for a specific process id"); + + dbg(DBG_TEST, "Successfully removed proc 2 with pid: %d\n", new_proc2->p_pid); + + // wait for any child process + ret = do_waitpid(-1, &status, 0); + test_assert(ret == new_proc1->p_pid, "Waiting for any child process"); + dbg(DBG_TEST, "Successfully removed proc 1 with pid: %d\n", new_proc1->p_pid); + ret = do_waitpid(-1, &status, 0); + test_assert(ret == new_proc3->p_pid, "Waiting for any child process"); + dbg(DBG_TEST, "Successfully removed proc 3 with pid: %d\n", new_proc3->p_pid); + + ret = do_waitpid(-1, &status, 0); + test_assert(ret == -ECHILD, "No child processes to wait for"); +} + +/* + Tests proc_kill_all on a single process. +*/ +void *test_func_kill(long arg1, void *arg2) +{ + // function to be called by the process + proc_t *proc_as_arg = (proc_t *)arg2; + test_assert(arg1 == proc_as_arg->p_pid, "Arguments are not set up correctly"); + test_assert(proc_as_arg->p_state == PROC_RUNNING, "Process state is not running"); + test_assert(list_empty(&proc_as_arg->p_children), "There should be no child processes"); + dbg(DBG_TEST, "Process with pid=%d is running and calling proc_kill_all\n", proc_as_arg->p_pid); + proc_kill_all(); + return NULL; +} +void test_proc_kill_all() +{ + proc_t *new_proc_kill = proc_create("proc test kill"); + kthread_t *new_kthread_kill = kthread_create(new_proc_kill, test_func_kill, new_proc_kill->p_pid, new_proc_kill); + sched_make_runnable(new_kthread_kill); + + proc_t *new_proc1 = proc_create("proc test 1"); + kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1); + sched_make_runnable(new_kthread1); + + proc_t *new_proc2 = proc_create("proc test 2"); + kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2); + sched_make_runnable(new_kthread2); + + // wait on the process that calls proc_kill_all + int status; + int ret = do_waitpid(new_proc_kill->p_pid, &status, 0); + test_assert(ret == new_proc_kill->p_pid, "Waiting for a specific process id"); + dbg(DBG_TEST, "Successfully removed proc_kill with pid: %d\n", new_proc_kill->p_pid); + + // wait for all the threads to finish + int count = 0; + while (do_waitpid(-1, &status, 0) != -ECHILD) + { + dbg(DBG_TEST, "Waiting for child process to terminate\n"); + test_assert(status == 0, "Returned status not set correctly"); + count++; + } + test_assert(count == 2, + "Expected: %d, Actual: %d number of processes have been cleaned up\n", 2, count); + +} + +/* + Test to run several threads and processes concurrently. +*/ +void test_multiple() +{ + int num_procs_created = 0; + proc_t *new_proc1 = proc_create("proc test 1"); + kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1); + num_procs_created++; + sched_make_runnable(new_kthread1); + + proc_t *new_proc2 = proc_create("proc test 2"); + kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2); + num_procs_created++; + sched_make_runnable(new_kthread2); + + proc_t *new_proc3 = proc_create("proc test 3"); + kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3); + num_procs_created++; + sched_make_runnable(new_kthread3); + + // print all of the threads in the system + list_iterate(&curproc->p_threads, thread, kthread_t, kt_plink) + { + dbg(DBG_THR, "Thread: %s\n", thread->kt_proc->p_name); + } + + // wait for all the threads to finish + int count = 0; + int status; + while (do_waitpid(-1, &status, 0) != -ECHILD) + { + dbg(DBG_TEST, "Waiting for child process to terminate\n"); + test_assert(status == 0, "Returned status not set correctly"); + count++; + } + test_assert(count == num_procs_created, + "Expected: %d, Actual: %d number of processes have been cleaned up\n", num_procs_created, count); +} + +/* + Test that creates several child processes and forces them to terminate out of order. + Then it checks to see if the processes are cleaned up correctly. +*/ +void *func_forever_yielding(long arg1, void *arg2) +{ + // function that always just yields + // goal is that it is cleaned up by a different process + while (1) + { + // have a way to stop the thread + int *to_stop = (int *)arg2; + if (*to_stop) + { + dbg(DBG_TEST, "Thread with pid=%d is stopping yield loop\n", curproc->p_pid); + do_exit(1); // return different status code + break; + } + // wait a bit + for (int i = 0; i < 1000000; i++) + { + ; + } + dbg(DBG_TEST, "Thread with pid=%d is yielding on it's parent\n", curproc->p_pid); + sched_yield(); + } + return NULL; +} +void test_out_of_order_termination() +{ + // create a yielding proc that will last for a long time + proc_t *new_proc1 = proc_create("yieling proc 1"); + int stop_func_1 = 0; + kthread_t *new_kthread1 = kthread_create(new_proc1, func_forever_yielding, new_proc1->p_pid, &stop_func_1); + sched_make_runnable(new_kthread1); + + proc_t *new_proc2 = proc_create("yielding proc 2"); + int stop_func_2 = 0; + kthread_t *new_kthread2 = kthread_create(new_proc2, func_forever_yielding, new_proc2->p_pid, &stop_func_2); + sched_make_runnable(new_kthread2); + + proc_t *new_proc3 = proc_create("proc test 3"); + kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3); + sched_make_runnable(new_kthread3); + + proc_t *new_proc4 = proc_create("proc test 4"); + kthread_t *new_kthread4 = kthread_create(new_proc4, test_func, new_proc4->p_pid, new_proc4); + sched_make_runnable(new_kthread4); + + // let to first and second procs go and yield + sched_yield(); + + // the first and second proc should yield to the third proc and fourth proc + // which will return here to the init proc + test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc"); + test_assert(new_proc3->p_state == PROC_DEAD, "Third proc should be dead"); + test_assert(new_proc4->p_state == PROC_DEAD, "Fourth proc should be dead"); + + // destroy procs 3 and 4 + + // terminate the second proc + stop_func_2 = 1; + sched_yield(); + + // we should return back to the init proc + test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc"); + test_assert(new_proc2->p_state == PROC_DEAD, "Second proc should be dead"); + + // terminate the first proc + stop_func_1 = 1; + sched_yield(); + + // we should return back to the init proc + test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc"); + test_assert(new_proc1->p_state == PROC_DEAD, "First proc should be dead"); + + // clean up proc 2 using do_waitpid, with specific status 1 + int status; + int ret = do_waitpid(new_proc2->p_pid, &status, 0); + test_assert(ret == new_proc2->p_pid, "wrong specific process id"); + test_assert(status == 1, "Returned status not set correctly"); + test_assert(new_proc2->p_status == 1, "Status not set correctly"); + + // clean up the rest of the dead procs using do_waitpid + int count = 0; + while ((ret = do_waitpid(-1, &status, 0)) != -ECHILD) + { + dbg(DBG_TEST, "found child with pid=%d that needs to be cleaned up\n", ret); + if (ret == new_proc1->p_pid) { + test_assert(status == 1, "Returned status not set correctly for proc 1 with pid=%d", new_proc1->p_pid); + } else { + test_assert(status == 0, "Returned status not set correctly"); + } + count++; + } + test_assert(count == 3, + "Expected: %d, Actual: %d number of processes have been cleaned up\n", 3, count); +} + +/* + Test threads' cancellation fields. +*/ +void *did_run_func(long arg1, void *arg2) +{ + // arg2 is a boolean flag that indicated this ran + *(int *)arg2 = 1; + return NULL; +} +void test_cancellation() +{ + proc_t *new_proc1 = proc_create("proc test 1"); + int did_run = 0; + kthread_t *new_kthread1 = kthread_create(new_proc1, did_run_func, new_proc1->p_pid, (void *)&did_run); + test_assert(new_kthread1->kt_cancelled == 0, "Thread should not be cancelled"); + sched_make_runnable(new_kthread1); + + proc_kill(new_proc1, 1); + + test_assert(new_kthread1->kt_cancelled == 1, "Thread should be cancelled"); + + // wait for the thread to finish + int status; + int ret = do_waitpid(new_proc1->p_pid, &status, 0); + test_assert(ret != -ECHILD, "Should have found the process"); + test_assert(ret == new_proc1->p_pid, "Should have found the correct process"); + test_assert(status == 1, "Returned status not set correctly"); + test_assert(did_run == 0, "Thread should not have run if it was cancelled"); +} + + long proctest_main(long arg1, void *arg2) { dbg(DBG_TEST, "\nStarting Procs tests\n"); @@ -51,6 +312,16 @@ long proctest_main(long arg1, void *arg2) // Add more tests here! // We highly recommend looking at section 3.8 on the handout for help! + dbg(DBG_TEST, "\nStarting test_multiple\n"); + test_multiple(); + dbg(DBG_TEST, "\nStarting test_do_waitpid\n"); + test_do_waitpid(); + dbg(DBG_TEST, "\nStarting test_proc_kill_all\n"); + test_proc_kill_all(); + dbg(DBG_TEST, "\nStarting test_out_of_order_termination\n"); + test_out_of_order_termination(); + dbg(DBG_TEST, "\nStarting test_cancellation\n"); + test_cancellation(); test_fini(); return 0; |