aboutsummaryrefslogtreecommitdiff
path: root/kernel/test/proctest.c
diff options
context:
space:
mode:
authorsotech117 <michael_foiani@brown.edu>2024-02-20 18:23:50 +0000
committersotech117 <michael_foiani@brown.edu>2024-02-20 18:23:50 +0000
commitd2b13e08f8d6111b7f7e79a47a124fc47dd9433c (patch)
treeec46a99622c40dae1d45ff516699f949193275a2 /kernel/test/proctest.c
parent84aae99b4f2f4997db95b9ea6db2a61f582b51ea (diff)
make tests and fix bugs that the tests caught :)
Diffstat (limited to 'kernel/test/proctest.c')
-rw-r--r--kernel/test/proctest.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/kernel/test/proctest.c b/kernel/test/proctest.c
index eb70c81..9ab4c65 100644
--- a/kernel/test/proctest.c
+++ b/kernel/test/proctest.c
@@ -45,6 +45,235 @@ 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);
+}
+
+
long proctest_main(long arg1, void *arg2)
{
dbg(DBG_TEST, "\nStarting Procs tests\n");
@@ -53,6 +282,14 @@ 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();
test_fini();
return 0;