aboutsummaryrefslogtreecommitdiff
path: root/kernel/test
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/test')
-rw-r--r--kernel/test/proctest.c271
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;