diff options
Diffstat (limited to 'kernel/test/driverstest.c')
-rw-r--r-- | kernel/test/driverstest.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/kernel/test/driverstest.c b/kernel/test/driverstest.c new file mode 100644 index 0000000..0ed5e1d --- /dev/null +++ b/kernel/test/driverstest.c @@ -0,0 +1,288 @@ +#include "errno.h" +#include "globals.h" + +#include "test/usertest.h" +#include "test/proctest.h" + +#include "util/debug.h" +#include "util/printf.h" +#include "util/string.h" + +#include "proc/proc.h" +#include "proc/kthread.h" +#include "proc/sched.h" + +#include "drivers/tty/tty.h" +#include "drivers/dev.h" +#include "drivers/blockdev.h" +#include "drivers/keyboard.h" + +#define TEST_STR_1 "hello\n" +#define TEST_STR_2 "different string\n" +#define TEST_STR_3 "test" +#define TEST_BUF_SZ 10 +#define NUM_PROCS 3 +#define BLOCK_NUM 0 + +// TODO: need to change to using the MOD macro + +void* kthread_write(long arg1, void* arg2) { + chardev_t* cd = chardev_lookup(MKDEVID(TTY_MAJOR, arg1)); + tty_t* tty = cd_to_tty(cd); + + int count = 0; + while (count < 2) { + if (count == 0) { + for (size_t i = 0; i < strlen(TEST_STR_1); i++) { + ldisc_key_pressed(&tty->tty_ldisc, TEST_STR_1[i]); + } + } else { + for (size_t i = 0; i < strlen(TEST_STR_2); i++) { + ldisc_key_pressed(&tty->tty_ldisc, TEST_STR_2[i]); + } + } + count++; + } + return NULL; +} + +void* kthread_read1(long arg1, void* arg2) { + chardev_t* cd = chardev_lookup(MKDEVID(TTY_MAJOR, arg1)); + char buf[32]; + memset(buf, 0, 32); + size_t num_bytes = cd->cd_ops->read(cd, 0, buf, strlen(TEST_STR_1)); + test_assert(num_bytes == strlen(TEST_STR_1), "number of bytes is incorrect"); + test_assert(!strncmp(buf, TEST_STR_1, strlen(TEST_STR_1)), "resulting strings are not equal"); + + return NULL; +} + +void* kthread_read2(long arg1, void* arg2) { + chardev_t* cd = chardev_lookup(MKDEVID(TTY_MAJOR, arg1)); + + char buf[32]; + memset(buf, 0, 32); + size_t num_bytes = cd->cd_ops->read(cd, 0, buf, strlen(TEST_STR_2)); + test_assert(num_bytes == strlen(TEST_STR_2), "number of bytes is incorrect"); + test_assert(!strncmp(buf, TEST_STR_2, strlen(TEST_STR_2)), "resulting strings are not equal"); + + return NULL; +} + +long test_concurrent_reads() { + proc_t* proc_write = proc_create("process_write"); + kthread_t* kt_write = kthread_create(proc_write, kthread_write, 0, NULL); + + proc_t* proc_1 = proc_create("process_1_read"); + kthread_t* kthread_1 = kthread_create(proc_1, kthread_read1, 0, NULL); + + proc_t* proc_2 = proc_create("process_2_read"); + kthread_t* kthread_2 = kthread_create(proc_2, kthread_read2, 0, NULL); + + sched_make_runnable(kthread_1); + sched_make_runnable(kthread_2); + sched_make_runnable(kt_write); + + while (do_waitpid(-1, NULL, 0) != -ECHILD) + ; + + return 0; +} + +/** + * Function for each kthread to write the order in which they were spawned + * to the character device. +*/ +void* kthread_concurrent_write(long arg1, void* arg2) { + chardev_t* cd = chardev_lookup(MKDEVID(TTY_MAJOR, 0)); + char buf[32]; + memset(buf, 0, 32); + snprintf(buf, 32, "thread_%d\n", (int)arg1); + size_t num_bytes = cd->cd_ops->write(cd, 0, buf, strlen(buf)); + test_assert(num_bytes == strlen(buf), "number of bytes written is not correct"); + return NULL; +} + +long test_concurrent_writes() { + char proc_name[32]; + for (int i = 0; i < NUM_PROCS; i++) { + memset(proc_name, 0, 32); + snprintf(proc_name, 32, "process_concurrent_write_%d", i); + proc_t* proc_write = proc_create(proc_name); + kthread_t* kt_write = kthread_create(proc_write, kthread_concurrent_write, i, NULL); + sched_make_runnable(kt_write); + } + + while (do_waitpid(-1, NULL, 0) != -ECHILD) + ; + + return 0; +} + +void* kthread_write_disk(long arg1, void* arg2) { + // write to disk here + void* page_of_data = page_alloc(); + // memset it to be some random character + memset(page_of_data, 'F', BLOCK_SIZE); + blockdev_t* bd = blockdev_lookup(MKDEVID(DISK_MAJOR, 0)); + long ret = bd->bd_ops->write_block(bd, (char*)page_of_data, arg1, 1); + test_assert(ret == 0, "the write operation failed"); + + return NULL; +} + +void* kthread_read_disk(long arg1, void* arg2) { + // read that same block of data here + // not going to memset it because we are reading that amount + void* page_of_data_to_read = page_alloc_n(2); + void* data_expected = page_alloc_n(2); + memset(data_expected, 'F', BLOCK_SIZE); + blockdev_t* bd = blockdev_lookup(MKDEVID(DISK_MAJOR, 0)); + test_assert(!PAGE_ALIGNED((char*)page_of_data_to_read+1), "not page aligned"); + long ret = bd->bd_ops->read_block(bd, (char*)page_of_data_to_read+1, arg1, 1); + test_assert(ret == 0, "the read operation failed"); + test_assert(0 == memcmp((char*)page_of_data_to_read+1, data_expected, BLOCK_SIZE), "bytes are not equal"); + page_free_n(page_of_data_to_read, 2); + page_free_n(data_expected, 2); + return NULL; +} + +/* + First write to disk and then attempt to read from disk +*/ +long test_disk_write_and_read() { + proc_t* proc_write = proc_create("process_write"); + kthread_t* kt_write = kthread_create(proc_write, kthread_write_disk, BLOCK_NUM, NULL); + + proc_t* proc_read = proc_create("process_read"); + kthread_t* kt_read = kthread_create(proc_read, kthread_read_disk, BLOCK_NUM, NULL); + + sched_make_runnable(kt_write); + sched_make_runnable(kt_read); + + while (do_waitpid(-1, NULL, 0) != -ECHILD) + ; + + return 0; +} + +/* + Tests inputting a character and a newline character +*/ +long test_basic_line_discipline() { + chardev_t* cd = chardev_lookup(MKDEVID(TTY_MAJOR, 0)); + tty_t* tty = cd_to_tty(cd); + ldisc_t* ldisc = &tty->tty_ldisc; + ldisc_key_pressed(ldisc, 't'); + + test_assert(ldisc->ldisc_buffer[ldisc->ldisc_tail] == 't', "character not inputted into buffer correctly"); + test_assert(ldisc->ldisc_head != ldisc->ldisc_cooked && ldisc->ldisc_tail != ldisc->ldisc_head, "pointers are updated correctly"); + + size_t previous_head_val = ldisc->ldisc_head; + ldisc_key_pressed(ldisc, '\n'); + test_assert(ldisc->ldisc_head == previous_head_val + 1, "ldisc_head should have been incremented past newline character"); + test_assert(ldisc->ldisc_cooked == ldisc->ldisc_head, "ldisc_cooked should be equal to ldisc_head"); + + // reset line discipline for other tests before returning + ldisc->ldisc_head = ldisc->ldisc_cooked = ldisc->ldisc_tail = 0; + return 0; +} + +/* + Tests removing a character +*/ +long test_backspace() { + chardev_t* cd = chardev_lookup(MKDEVID(TTY_MAJOR, 0)); + tty_t* tty = cd_to_tty(cd); + ldisc_t* ldisc = &tty->tty_ldisc; + size_t previous_head_val = ldisc->ldisc_head; + ldisc_key_pressed(ldisc, 't'); + ldisc_key_pressed(ldisc, '\b'); + test_assert(ldisc->ldisc_head == previous_head_val, "Backspace should move the ldisc_head back by 1"); + + // testing there should be no characters to remove + ldisc_key_pressed(ldisc, '\b'); + test_assert(ldisc->ldisc_head == previous_head_val, "This backspace should result in a no-op"); + + // reset line discipline for other tests before returning + ldisc->ldisc_head = ldisc->ldisc_cooked = ldisc->ldisc_tail = 0; + return 0; +} + +void* kthread_wait_for_eot(long arg1, void* arg2) { + chardev_t* cd = (chardev_t*)arg2; + char buf[32]; + memset(buf, 0, 32); + size_t num_bytes = cd->cd_ops->read(cd, 0, buf, TEST_BUF_SZ); + test_assert(num_bytes == strlen(TEST_STR_3), "number of bytes is incorrect"); + test_assert(!strncmp(buf, TEST_STR_3, strlen(TEST_STR_3)), "resulting strings are not equal"); + return NULL; +} + +/* + Tests the behavior for EOT +*/ +long test_eot() { + chardev_t* cd = chardev_lookup(MKDEVID(TTY_MAJOR, 0)); + tty_t* tty = cd_to_tty(cd); + ldisc_t* ldisc = &tty->tty_ldisc; + + proc_t* proc_read = proc_create("process_read"); + kthread_t* kt_read = kthread_create(proc_read, kthread_wait_for_eot, 0, cd); + sched_make_runnable(kt_read); + // allow the other process to run first so it can block before typing + sched_yield(); + + size_t prev_tail_value = ldisc->ldisc_tail; + for (size_t i = 0; i < strlen(TEST_STR_3); i++) { + ldisc_key_pressed(ldisc, TEST_STR_3[i]); + } + ldisc_key_pressed(ldisc, EOT); + test_assert(ldisc->ldisc_head == ldisc->ldisc_cooked, "ldisc_head should be equal to ldisc_cooked"); + + // allow the other thread to read + while (do_waitpid(-1, NULL, 0) != -ECHILD) + ; + test_assert(ldisc->ldisc_tail == prev_tail_value + strlen(TEST_STR_3) + 1, "ldisc_tail should be past the EOT char"); + ldisc->ldisc_head = ldisc->ldisc_tail = ldisc->ldisc_cooked = 0; + return 0; +} + +/* + Tests the behavior for ETX +*/ +long test_etx() { + chardev_t* cd = chardev_lookup(MKDEVID(TTY_MAJOR, 0)); + tty_t* tty = cd_to_tty(cd); + ldisc_t* ldisc = &tty->tty_ldisc; + size_t previous_head_value = ldisc->ldisc_head; + + // "press" two characters + ldisc_key_pressed(ldisc, 't'); + ldisc_key_pressed(ldisc, 'e'); + ldisc_key_pressed(ldisc, ETX); + + test_assert(previous_head_value + 1 == ldisc->ldisc_head, "ldisc_head should only be one past where it used to be"); + test_assert(ldisc->ldisc_head == ldisc->ldisc_cooked, "ldisc should be a cooked blank line"); + + // reset line discipline for other tests before returning + ldisc->ldisc_head = ldisc->ldisc_cooked = ldisc->ldisc_tail = 0; + return 0; +} + +long driverstest_main(long arg1, void* arg2) +{ + dbg(DBG_TEST, "\nStarting Drivers tests\n"); + test_init(); + + test_basic_line_discipline(); + test_backspace(); + test_eot(); + test_etx(); + test_concurrent_reads(); + test_concurrent_writes(); + test_disk_write_and_read(); + + test_fini(); + return 0; +}
\ No newline at end of file |