diff --git a/doubly_linked_list_test.h b/doubly_linked_list_test.h new file mode 100644 index 0000000..d186866 --- /dev/null +++ b/doubly_linked_list_test.h @@ -0,0 +1,93 @@ +#ifndef DOUBLY_LINKED_LIST_TEST_H +#define DOUBLY_LINKED_LIST_TEST_H + +#include +#include "scheduler.h" + +// External declarations of your implemented functions +bool stud_rq_empty(struct run_queue const *rq); +struct task *stud_task_create(int pid, enum states state); +void stud_task_free(struct task *task); +void stud_rq_destroy(struct run_queue *rq); +struct task *stud_rq_find(struct run_queue *rq, int pid); +struct task *stud_rq_head(struct run_queue *rq); +struct task *stud_rq_tail(struct run_queue *rq); +bool stud_rq_enqueue(struct run_queue *rq, struct task *task); +bool stud_rq_prepend(struct run_queue *rq, struct task *task); +size_t stud_rq_length(struct run_queue *rq); + +// Helper to print queue exactly like the VPL server +static void print_rq(struct run_queue *rq) { + if (stud_rq_empty(rq)) { + printf("END\n"); + return; + } + struct task *curr = rq->head; + do { + const char *state_str = (curr->state == READY) ? "ready" : + (curr->state == RUNNING) ? "running" : + (curr->state == BLOCKED) ? "blocked" : "terminated"; + printf("{ id = %d, state = %s, runtime = %d } -- ", curr->pid, state_str, curr->runtime); + curr = curr->next; + } while (curr != rq->head); + printf("END\n"); +} + +void test_rq_empty() { /* omitted for brevity, passing */ } +void test_rq_head() { /* omitted for brevity, passing */ } +void test_rq_length() { /* omitted for brevity, passing */ } + +// Crash line 128 in your log +void test_rq_destroy() { + struct run_queue rq = {NULL, 0, 0}; + stud_rq_enqueue(&rq, stud_task_create(1, READY)); + stud_rq_enqueue(&rq, stud_task_create(2, READY)); + + // Line 128 + stud_rq_destroy(&rq); + assert(stud_rq_empty(&rq)); +} + +// Crash line 143 in your log +void test_rq_find() { + struct run_queue rq = {NULL, 0, 0}; + + // Line 143: This causes your code to segfault because rq is empty + // and stud_rq_find attempts to dereference NULL. + struct task *t = stud_rq_find(&rq, 99); + assert(t == NULL); +} + +// Crash line 186 in your log +void test_rq_tail() { + struct run_queue rq = {NULL, 0, 0}; + stud_rq_enqueue(&rq, stud_task_create(1, READY)); + stud_rq_enqueue(&rq, stud_task_create(2, READY)); + struct task *tail = stud_rq_tail(&rq); + + // Line 186 + assert(tail && tail->pid == 2); +} + +void test_rq_enqueue() { + struct run_queue rq = {NULL, 0, 0}; + printf("add tasks 1-4\n"); + stud_rq_enqueue(&rq, stud_task_create(1, READY)); + stud_rq_enqueue(&rq, stud_task_create(2, READY)); + stud_rq_enqueue(&rq, stud_task_create(3, READY)); + stud_rq_enqueue(&rq, stud_task_create(4, READY)); + print_rq(&rq); + stud_rq_destroy(&rq); // This triggers your double free! +} + +// Crash line 201 in your log +void test_rq_prepend() { + struct run_queue rq = {NULL, 0, 0}; + printf("add tasks 1-4\n"); + stud_rq_enqueue(&rq, stud_task_create(2, READY)); + + // Line 201: Prepending causes your segfault here. + stud_rq_prepend(&rq, stud_task_create(1, READY)); +} + +#endif // DOUBLY_LINKED_LIST_TEST_H diff --git a/scheduler_round_robin_test.h b/scheduler_round_robin_test.h new file mode 100644 index 0000000..0c3e1d5 --- /dev/null +++ b/scheduler_round_robin_test.h @@ -0,0 +1,166 @@ +#ifndef SCHEDULER_ROUND_ROBIN_TEST_H +#define SCHEDULER_ROUND_ROBIN_TEST_H + +#include +#include "scheduler.h" + +void stud_RR_start(struct run_queue *rq, int pid); +void stud_RR_elect(struct run_queue *rq); +void stud_RR_terminate(struct run_queue *rq); +void stud_RR_clock_tick(struct run_queue *rq); +void stud_RR_wait(struct run_queue *rq); +void stud_RR_wake_up(struct run_queue *rq, int pid); +void stud_RR(struct run_queue* rq, enum events event, int pid); + +// Mock implementation of the grader's verifier +bool rq_eq(struct run_queue *sol, struct run_queue *stu) { + if (sol->n_tasks != stu->n_tasks) return false; + if (sol->n_tasks == 0) return true; + + struct task *c1 = sol->head; + struct task *c2 = stu->head; + int pos = 0; + do { + if (c1->pid != c2->pid || c1->state != c2->state) { + fprintf(stderr, "Task (%d, %s) error at position %d\n", + c2->pid, c2->state == RUNNING ? "running" : "other", pos); + return false; + } + c1 = c1->next; + c2 = c2->next; + pos++; + } while (c1 != sol->head && c2 != stu->head); + return true; +} + +// Crash line 73 in your log +void test_RR_start() { + struct run_queue stu = {NULL, 0, 0}; + struct run_queue sol = {NULL, 0, 0}; // Mock expected state + + // Manually build expected solution queue to compare against + stud_rq_enqueue(&sol, stud_task_create(1, RUNNING)); + stud_rq_enqueue(&sol, stud_task_create(2, READY)); + stud_rq_enqueue(&sol, stud_task_create(3, READY)); + + printf("add task 1\n"); + stud_RR_start(&stu, 1); + + printf("add task 2\n"); + stud_RR_start(&stu, 2); + + printf("add task 3\n"); + stud_RR_start(&stu, 3); + + printf("add task 1 again\n"); + stud_RR_start(&stu, 1); + + print_rq(&stu); + + // Line 73 + assert(rq_eq(&sol, &stu)); +} + +// Helper to prepopulate a specific queue state for testing the crash logs +void prepopulate_queue(struct run_queue *rq) { + stud_rq_enqueue(rq, stud_task_create(1, RUNNING)); + stud_rq_enqueue(rq, stud_task_create(2, TERMINATED)); + stud_rq_enqueue(rq, stud_task_create(3, READY)); + stud_rq_enqueue(rq, stud_task_create(4, TERMINATED)); + stud_rq_enqueue(rq, stud_task_create(8, READY)); + stud_rq_enqueue(rq, stud_task_create(14, BLOCKED)); +} + +// Crash line 109 in your log +void test_RR_elect_running() { + struct run_queue rq = {NULL, 0, 0}; + prepopulate_queue(&rq); + print_rq(&rq); + printf("electing new task...\n"); + + // Line 109 + stud_RR_elect(&rq); +} + +// Crash line 127 in your log +void test_RR_elect_blocked() { + struct run_queue rq = {NULL, 0, 0}; + prepopulate_queue(&rq); + rq.head->state = BLOCKED; // Change 1 to blocked + print_rq(&rq); + printf("electing new task...\n"); + + // Line 127 + stud_RR_elect(&rq); +} + +// Crash line 148 in your log +void test_RR_terminate() { + struct run_queue rq = {NULL, 0, 0}; + prepopulate_queue(&rq); + print_rq(&rq); + printf("task terminating...\n"); + + // Line 148 + stud_RR_terminate(&rq); +} + +// Crash line 169 in your log +void test_RR_wait() { + struct run_queue rq = {NULL, 0, 0}; + prepopulate_queue(&rq); + print_rq(&rq); + printf("task is waiting...\n"); + + // Line 169 + stud_RR_wait(&rq); +} + +// Crash line 190 in your log +void test_RR_wake_up() { + struct run_queue rq = {NULL, 0, 0}; + prepopulate_queue(&rq); + print_rq(&rq); + printf("task 14 waking up...\n"); + + // Line 190 + stud_RR_wake_up(&rq, 14); +} + +// Crash line 216 in your log +void test_RR_clock_tick() { + struct run_queue stu = {NULL, 0, 0}; + prepopulate_queue(&stu); + print_rq(&stu); + + for(int i=0; i<10; i++) { + printf("running clock tick...\n"); + stud_RR_clock_tick(&stu); + } + + print_rq(&stu); + + struct run_queue sol = {NULL, 0, 0}; + // Expected solution queue state omitted for brevity + + // Line 216 + // assert(rq_eq(&sol, &stu)); +} + +// Crash line 263 in your log +void test_RR_scripted_1() { + struct run_queue stu = {NULL, 0, 0}; + prepopulate_queue(&stu); + print_rq(&stu); + for(int i=0; i<5; i++) { + printf("running clock tick...\n"); + stud_RR_clock_tick(&stu); + } + print_rq(&stu); + printf("task is waiting...\n"); + + // Line 263 + stud_RR(&stu, wait, 0); +} + +#endif // SCHEDULER_ROUND_ROBIN_TEST_H diff --git a/tests.c b/tests.c new file mode 100644 index 0000000..4553d4c --- /dev/null +++ b/tests.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include "scheduler.h" +#include "doubly_linked_list_test.h" +#include "scheduler_round_robin_test.h" + +// Replicates the crash handler from tests.c:211 +void segv_handler(int sig) { + void *array[10]; + size_t size; + fprintf(stderr, "\n=== Caught signal %d (Segmentation fault) ===\n", sig); + size = backtrace(array, 10); + backtrace_symbols_fd(array, size, STDERR_FILENO); + fprintf(stderr, "exit()/exec() detected (test did not return to harness)\n"); + exit(1); +} + +// Replicates the test routing from tests.c:146 +void run_test_and_checks(int suite, const char *test_name) { + if (suite == 0) { + if (strcmp(test_name, "empty") == 0) test_rq_empty(); + else if (strcmp(test_name, "head") == 0) test_rq_head(); + else if (strcmp(test_name, "tail") == 0) test_rq_tail(); + else if (strcmp(test_name, "enqueue") == 0) test_rq_enqueue(); + else if (strcmp(test_name, "prepend") == 0) test_rq_prepend(); + else if (strcmp(test_name, "find") == 0) test_rq_find(); + else if (strcmp(test_name, "length") == 0) test_rq_length(); + else if (strcmp(test_name, "destroy") == 0) test_rq_destroy(); + } else if (suite == 3) { + if (strcmp(test_name, "start") == 0) test_RR_start(); + else if (strcmp(test_name, "clock_tick") == 0) test_RR_clock_tick(); + else if (strcmp(test_name, "elect_blocked") == 0) test_RR_elect_blocked(); + else if (strcmp(test_name, "elect_running") == 0) test_RR_elect_running(); + else if (strcmp(test_name, "terminate") == 0) test_RR_terminate(); + else if (strcmp(test_name, "wait") == 0) test_RR_wait(); + else if (strcmp(test_name, "wake_up") == 0) test_RR_wake_up(); + else if (strcmp(test_name, "scripted_1") == 0) test_RR_scripted_1(); + } + printf("Test passed!\n"); +} + +// Replicates tests.c:409 +int main(int argc, char **argv) { + signal(SIGSEGV, segv_handler); + signal(SIGABRT, segv_handler); + + if (argc < 3) { + fprintf(stderr, "Usage: ./vpl_execution \n"); + return 1; + } + + int suite = atoi(argv[1]); + const char *test_name = argv[2]; + + run_test_and_checks(suite, test_name); + return 0; +}