Browse Source

Merge branch 'unit_tests'

coderain 5 years ago
parent
commit
bb838ae01d
8 changed files with 490 additions and 169 deletions
  1. 1 1
      .gitignore
  2. 14 4
      common.mk
  3. 4 7
      tests/.gitignore
  4. 4 22
      tests/Makefile
  5. 0 135
      tests/hello.asm
  6. 33 0
      tests/unit_tests/Makefile
  7. 403 0
      tests/unit_tests/src/avltree.c
  8. 31 0
      tests/unit_tests/src/main.c

+ 1 - 1
.gitignore

@@ -20,7 +20,7 @@
 !library
 !library/*
 !tests
-!tests/*
+!tests/**
 !manager
 !manager/*
 !drivers

+ 14 - 4
common.mk

@@ -17,7 +17,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-ARCH := i686-elf
+ifndef ARCH
+	ARCH := i686-elf
+endif
 
 # Directories
 SRCDIR       := src
@@ -30,9 +32,17 @@ PROJECT_ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
 export PATH  := $(PROJECT_ROOT)/$(TOOLSDIR)/$(TOOLSROOTDIR)/bin:$(PATH)
 
 # Compilers and tools
-CC   := $(ARCH)-gcc
+
 ASM  := nasm
-LINK := $(ARCH)-ld
+
+ifneq ($(ARCH), host)
+	CC   := $(ARCH)-gcc
+	LINK := $(ARCH)-ld
+	LDFLAGS += -eprocess_startup
+else
+	CC   := gcc
+	LINK := gcc
+endif
 
 ifeq ($(DEBUG), yes)
     CFLAGS += -g -DDEBUG
@@ -80,7 +90,7 @@ $(OUTPUT_KERNEL): $(OBJECTS) $(ADDITIONAL_OBJECTS)
 	$(LINK) -o $@ $(OBJECTS) $(LDFLAGS)
 
 $(OUTPUT_PROGRAM): $(OBJECTS)
-	$(LINK) -eprocess_startup -o $@ $(OBJECTS) $(LDFLAGS)
+	$(LINK) -o $@ $(OBJECTS) $(LDFLAGS)
 
 $(OUTPUT_STATIC_LIB): $(OBJECTS)
 	$(AR) rcs $@ $^

+ 4 - 7
tests/.gitignore

@@ -1,7 +1,4 @@
-*
-
-# Include the following files:
-!.gitignore
-!Makefile
-!mkfat.sh
-!*.asm
+# Ignore output files
+unit_tests/obj
+unit_tests/dep
+unit_tests/run

+ 4 - 22
tests/Makefile

@@ -1,7 +1,7 @@
 #
 # Makefile
 #
-# Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+# Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
@@ -17,26 +17,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Compilers and tools
-ASM = nasm
-
-# Flags
-ASMFLAGS = -faout
-
-# Input and output files
-SOURCES = $(wildcard *.asm)
-OBJECTS = $(patsubst %.asm, %.prg, $(SOURCES))
-
-.PHONY: all clean
-
-all: floppy.img
-
-%.prg: %.asm Makefile
-	$(ASM) $(ASMFLAGS) -o $@ $<
-
-floppy.img: $(OBJECTS) mkfat.sh Makefile
-	./mkfat.sh 2>/dev/null
+all:
+	make -C unit_tests all
 
 clean:
-	find $(OBJDIR) -name \*.prg -delete
-	rm floppy.img
+	make -C unit_tests clean

+ 0 - 135
tests/hello.asm

@@ -1,135 +0,0 @@
-;
-; hello.asm
-;
-; Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
-;
-; This program is free software: you can redistribute it and/or modify
-; it under the terms of the GNU Affero General Public License as
-; published by the Free Software Foundation, either version 3 of the
-; License, or (at your option) any later version.
-;
-; This program is distributed in the hope that it will be useful,
-; but WITHOUT ANY WARRANTY; without even the implied warranty of
-; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-; GNU Affero General Public License for more details.
-;
-; You should have received a copy of the GNU Affero General Public License
-; along with this program.  If not, see <http://www.gnu.org/licenses/>.
-;
-
-SYSCALL_ALLOC_MEMORY              EQU 0
-SYSCALL_CLOCK_GET_TIME            EQU 1
-SYSCALL_CLOCK_SET_TIME            EQU 2
-SYSCALL_CLOSE_OBJECT              EQU 3
-SYSCALL_COMMIT_MEMORY             EQU 4
-SYSCALL_CREATE_MEMORY_SECTION     EQU 5
-SYSCALL_CREATE_PROCESS            EQU 6
-SYSCALL_CREATE_SEMAPHORE          EQU 7
-SYSCALL_CREATE_THREAD             EQU 8
-SYSCALL_CREATE_USER               EQU 9
-SYSCALL_DELETE_FILE               EQU 10
-SYSCALL_DELETE_USER               EQU 11
-SYSCALL_DEVICE_IOCTL              EQU 12
-SYSCALL_DUPLICATE_HANDLE          EQU 13
-SYSCALL_ENUM_PROCESSES            EQU 14
-SYSCALL_FLUSH_MEMORY_SECTION      EQU 15
-SYSCALL_FREE_MEMORY               EQU 16
-SYSCALL_GET_EXCEPTION_INFO        EQU 17
-SYSCALL_GET_MILLISECONDS          EQU 18
-SYSCALL_GET_NANOSECONDS           EQU 19
-SYSCALL_GET_PROCESS_ID            EQU 20
-SYSCALL_GET_THREAD_ID             EQU 21
-SYSCALL_GET_USER_ID               EQU 22
-SYSCALL_LIST_DIRECTORY            EQU 23
-SYSCALL_LOGON_USER                EQU 24
-SYSCALL_MAP_MEMORY_SECTION        EQU 25
-SYSCALL_MOUNT                     EQU 26
-SYSCALL_OPEN_FILE                 EQU 27
-SYSCALL_OPEN_MEMORY_SECTION       EQU 28
-SYSCALL_OPEN_PIPE                 EQU 29
-SYSCALL_OPEN_PROCESS              EQU 30
-SYSCALL_OPEN_THREAD               EQU 31
-SYSCALL_POWER_CONTROL             EQU 32
-SYSCALL_QUERY_FILE                EQU 33
-SYSCALL_QUERY_HANDLE              EQU 34
-SYSCALL_QUERY_PROCESS             EQU 35
-SYSCALL_QUERY_THREAD              EQU 36
-SYSCALL_QUERY_USER                EQU 37
-SYSCALL_RAISE_EXCEPTION           EQU 38
-SYSCALL_READ_FILE                 EQU 39
-SYSCALL_READ_MEMORY               EQU 40
-SYSCALL_READ_PIPE                 EQU 41
-SYSCALL_RELEASE_SEMAPHORE         EQU 42
-SYSCALL_RESTORE_EXCEPTION_HANDLER EQU 43
-SYSCALL_REVERT_USER               EQU 44
-SYSCALL_SAVE_EXCEPTION_HANDLER    EQU 45
-SYSCALL_SET_MEMORY_FLAGS          EQU 46
-SYSCALL_SET_USER_ID               EQU 47
-SYSCALL_SLEEP                     EQU 48
-SYSCALL_TERMINATE                 EQU 49
-SYSCALL_TERMINATE_THREAD          EQU 50
-SYSCALL_UNCOMMIT_MEMORY           EQU 51
-SYSCALL_UNMOUNT                   EQU 52
-SYSCALL_WAIT_PROCESS              EQU 53
-SYSCALL_WAIT_SEMAPHORE            EQU 54
-SYSCALL_WAIT_THREAD               EQU 55
-SYSCALL_WRITE_FILE                EQU 56
-SYSCALL_WRITE_MEMORY              EQU 57
-SYSCALL_WRITE_PIPE                EQU 58
-SYSCALL_YIELD_QUANTUM             EQU 59
-
-bits 32
-
-section .text
-
-start:                  push 0
-                        push 0
-                        push handle
-                        push name
-                        push SYSCALL_OPEN_FILE
-                        call syscall
-                        add esp, 20
-                        push 4
-                        push address
-                        push 12
-                        push framebuffer
-                        push 0xC9000004
-                        push dword [handle]
-                        push SYSCALL_DEVICE_IOCTL
-                        call syscall
-                        add esp, 28
-                        or eax, eax
-                        jnz .stop
-                        mov edi, dword [address]
-                        lea esi, [edi + 160]
-                        mov ecx, 960
-                        cld
-                        rep movsd
-                        mov eax, 0x07200720
-                        mov ecx, 40
-                        rep stosd
-                        sub edi, 160
-                        mov esi, message
-                        mov ecx, 13
-                        rep movsw
-.stop:                  xor eax, eax
-                        not eax
-                        push eax
-                        push eax
-                        push SYSCALL_SLEEP
-                        call syscall
-                        add esp, 12
-                        jmp .stop
-
-%include "../library/src/syscall.asm"
-
-section .data
-
-handle:                 DD 0
-name:                   DB '@Video0', 0
-framebuffer:            DD 0x10000000
-                        DD 0
-                        DD 80 * 25 * 2
-address:                DD 0
-message:                DB 'H', 7, 'e', 7, 'l', 7, 'l', 7, 'o', 7, ',', 7, ' ',
-                        DB  7, 'w', 7, 'o', 7, 'r', 7, 'l', 7, 'd', 7, '!', 7

+ 33 - 0
tests/unit_tests/Makefile

@@ -0,0 +1,33 @@
+#
+# Makefile
+#
+# Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Settings
+DEBUG := yes
+ARCH  := host
+
+# Flags
+CFLAGS := -Wall -Werror -I ../..
+LDFLAGS := -pthread -lcheck -lsubunit -lrt -lm
+
+# Input and output files
+SOURCES = $(shell find $(SRCDIR) -type f -name \*.c)
+
+OUTPUT_PROGRAM := run
+
+-include ../../common.mk

+ 403 - 0
tests/unit_tests/src/avltree.c

@@ -0,0 +1,403 @@
+/*
+ * Monolithium Unit Tests
+ * avltree.c
+ *
+ * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <check.h>
+#include <stdlib.h>
+#include <sdk/avltree.h>
+
+#define SIZE 10000
+
+typedef int key_t;
+typedef int value_t;
+
+typedef struct
+{
+    avl_node_t node;
+    key_t key;
+    value_t value;
+} map_entry_t;
+
+avl_tree_t tree;
+
+static int compare(const void *k1, const void *k2)
+{
+    key_t a = *(key_t*)k1, b = *(key_t*)k2;
+    return a - b;
+}
+
+static int height(avl_node_t *root)
+{
+    if (!root) return 0;
+    int left = height(root->left);
+    int right = height(root->right);
+    return 1 + (left > right ? left : right);
+}
+
+static const void *minimum(avl_node_t *root)
+{
+    const void *result = avl_get_keyptr(&tree, root);
+
+    if (root->left)
+    {
+        const void *left = minimum(root->left);
+        if (compare(left, result) < 0) result = left;
+    }
+
+    if (root->right)
+    {
+        const void *right = minimum(root->right);
+        if (compare(right, result) < 0) result = right;
+    }
+
+    return result;
+}
+
+static const void *maximum(avl_node_t *root)
+{
+    const void *result = avl_get_keyptr(&tree, root);
+
+    if (root->left)
+    {
+        const void *left = maximum(root->left);
+        if (compare(left, result) > 0) result = left;
+    }
+
+    if (root->right)
+    {
+        const void *right = maximum(root->right);
+        if (compare(right, result) > 0) result = right;
+    }
+
+    return result;
+}
+
+static void test_integrity(avl_node_t *root, avl_node_t *parent)
+{
+    if (!root) return;
+    ck_assert(root->parent == parent);
+    test_integrity(root->left, root);
+    test_integrity(root->right, root);
+}
+
+static void test_order(avl_node_t *root)
+{
+    if (!root) return;
+    const void *key = avl_get_keyptr(&tree, root);
+    test_order(root->left);
+    if (root->left) ck_assert(compare(maximum(root->left), key) < 0);
+    if (root->right) ck_assert(compare(minimum(root->right), key) > 0);
+    test_order(root->right);
+}
+
+static void test_balance(avl_node_t *root)
+{
+    if (!root) return;
+    test_balance(root->left);
+
+    int balance = height(root->right) - height(root->left);
+    ck_assert(root->balance == balance);
+    ck_assert(balance >= -1 && balance <= 1);
+
+    test_balance(root->right);
+}
+
+static map_entry_t *map_lookup(key_t key)
+{
+    avl_node_t *node = avl_tree_lookup(&tree, &key);
+    return node ? CONTAINER_OF(node, map_entry_t, node) : NULL;
+}
+
+static void map_insert(key_t key, value_t value)
+{
+    map_entry_t *entry = malloc(sizeof(map_entry_t));
+    entry->key = key;
+    entry->value = value;
+    avl_tree_insert(&tree, &entry->node);
+}
+
+static void setup(void)
+{
+    AVL_TREE_INIT(&tree, map_entry_t, node, key, compare);
+}
+
+static void teardown(void)
+{
+}
+
+START_TEST(test_avl_tree_insert_ascending)
+{
+    int i;
+    for (i = 0; i < SIZE; i++) map_insert(i, i * 2);
+
+    test_integrity(tree.root, NULL);
+    test_order(tree.root);
+    test_balance(tree.root);
+
+    for (i = 0; i < SIZE; i++)
+    {
+        map_entry_t *entry = map_lookup(i);
+        ck_assert(entry != NULL && entry->value == i * 2);
+    }
+}
+END_TEST
+
+START_TEST(test_avl_tree_insert_descending)
+{
+    int i;
+    for (i = SIZE - 1; i >= 0; i--) map_insert(i, i * 2);
+
+    test_integrity(tree.root, NULL);
+    test_order(tree.root);
+    test_balance(tree.root);
+
+    for (i = 0; i < SIZE; i++)
+    {
+        map_entry_t *entry = map_lookup(i);
+        ck_assert(entry != NULL && entry->value == i * 2);
+    }
+}
+END_TEST
+
+START_TEST(test_avl_tree_insert_all_equal)
+{
+    int i;
+    for (i = SIZE - 1; i >= 0; i--) map_insert(i, 42);
+
+    test_integrity(tree.root, NULL);
+    test_order(tree.root);
+    test_balance(tree.root);
+
+    for (i = 0; i < SIZE; i++)
+    {
+        map_entry_t *entry = map_lookup(i);
+        ck_assert(entry != NULL && entry->value == 42);
+    }
+}
+END_TEST
+
+START_TEST(test_avl_tree_insert_random_order)
+{
+    int i;
+    int list[SIZE];
+    for (i = 0; i < SIZE; i++) list[i] = i;
+
+    for (i = 0; i < SIZE; i++)
+    {
+        int j = i + (rand() % (SIZE - i));
+        int t = list[i];
+        list[i] = list[j];
+        list[j] = t;
+    }
+
+    for (i = 0; i < SIZE; i++) map_insert(list[i], list[i] * 2);
+
+    test_integrity(tree.root, NULL);
+    test_order(tree.root);
+    test_balance(tree.root);
+
+    for (i = 0; i < SIZE; i++)
+    {
+        map_entry_t *entry = map_lookup(i);
+        ck_assert(entry != NULL);
+        ck_assert(entry->value == i * 2);
+    }
+}
+END_TEST
+
+START_TEST(test_avl_tree_remove_half_ascending)
+{
+    int i;
+    for (i = 0; i < SIZE; i++) map_insert(i, i * 2);
+
+    for (i = 0; i < SIZE / 2; i++)
+    {
+        map_entry_t *entry = map_lookup(i);
+        ck_assert(entry != NULL);
+        avl_tree_remove(&tree, &entry->node);
+        entry = map_lookup(i);
+        ck_assert(entry == NULL);
+    }
+
+    test_integrity(tree.root, NULL);
+    test_order(tree.root);
+    test_balance(tree.root);
+}
+END_TEST
+
+START_TEST(test_avl_tree_remove_random_half)
+{
+    int i;
+    for (i = 0; i < SIZE; i++) map_insert(i, i * 2);
+
+    for (i = 0; i < SIZE / 2; i++)
+    {
+        key_t key;
+        map_entry_t *entry = NULL;
+
+        while (!entry)
+        {
+            key = rand() % SIZE;
+            entry = map_lookup(key);
+        }
+
+        avl_tree_remove(&tree, &entry->node);
+        entry = map_lookup(key);
+        ck_assert(entry == NULL);
+    }
+
+    test_integrity(tree.root, NULL);
+    test_order(tree.root);
+    test_balance(tree.root);
+}
+END_TEST
+
+START_TEST(test_avl_tree_remove_half_with_duplicates)
+{
+    int i;
+    int counts[10] = {0};
+    for (i = 0; i < SIZE; i++)
+    {
+        map_insert(i % 10, (i % 10) * 2);
+        counts[i % 10]++;
+    }
+
+    for (i = 0; i < SIZE / 2; i++)
+    {
+        map_entry_t *entry = CONTAINER_OF(tree.root, map_entry_t, node);
+        key_t key = entry->key;
+        ck_assert(key >= 0 && key < 10);
+
+        avl_tree_remove(&tree, &entry->node);
+        counts[key]--;
+        ck_assert(counts[key] >= 0);
+
+        if (counts[key] == 0)
+        {
+            entry = map_lookup(key);
+            ck_assert(entry == NULL);
+        }
+    }
+
+    test_integrity(tree.root, NULL);
+    test_order(tree.root);
+    test_balance(tree.root);
+}
+END_TEST
+
+START_TEST(test_avl_tree_misc_traversal_forward)
+{
+    int i;
+    for (i = 0; i < SIZE; i++) map_insert(i, i * 2);
+
+    avl_node_t *node = tree.root;
+    while (node->left) node = node->left;
+
+    for (i = 0; i < SIZE; i++)
+    {
+        map_entry_t *entry = CONTAINER_OF(node, map_entry_t, node);
+        ck_assert(entry->key == i && entry->value == i * 2);
+        node = avl_get_next_node(node);
+    }
+}
+END_TEST
+
+START_TEST(test_avl_tree_misc_traversal_backward)
+{
+    int i;
+    for (i = 0; i < SIZE; i++) map_insert(i, i * 2);
+
+    avl_node_t *node = tree.root;
+    while (node->right) node = node->right;
+
+    for (i = SIZE - 1; i >= 0; i--)
+    {
+        map_entry_t *entry = CONTAINER_OF(node, map_entry_t, node);
+        ck_assert(entry->key == i && entry->value == i * 2);
+        node = avl_get_previous_node(node);
+    }
+}
+END_TEST
+
+START_TEST(test_avl_tree_misc_key_changing)
+{
+    int i;
+    for (i = 0; i < SIZE; i++) map_insert(i, (SIZE - i - 1) * 2);
+
+    for (i = 0; i < SIZE; i++)
+    {
+        map_entry_t *entry = map_lookup(i);
+        ck_assert(entry != NULL);
+        key_t new_key = SIZE - i - 1;
+        avl_tree_change_key(&tree, &entry->node, &new_key);
+    }
+
+    test_integrity(tree.root, NULL);
+    test_order(tree.root);
+    test_balance(tree.root);
+
+    for (i = 0; i < SIZE; i++)
+    {
+        map_entry_t *entry = map_lookup(i);
+        ck_assert(entry != NULL);
+        ck_assert(entry->value == i * 2);
+    }
+}
+END_TEST
+
+TCase *tcase_avl_tree_insertion_only_create(void)
+{
+    TCase *tc = tcase_create("Insertion Only");
+    tcase_add_checked_fixture(tc, setup, teardown);
+    tcase_add_test(tc, test_avl_tree_insert_ascending);
+    tcase_add_test(tc, test_avl_tree_insert_descending);
+    tcase_add_test(tc, test_avl_tree_insert_all_equal);
+    tcase_add_test(tc, test_avl_tree_insert_random_order);
+    return tc;
+}
+
+TCase *tcase_avl_tree_removal_create(void)
+{
+    TCase *tc = tcase_create("Removal");
+    tcase_add_checked_fixture(tc, setup, teardown);
+    tcase_add_test(tc, test_avl_tree_remove_half_ascending);
+    tcase_add_test(tc, test_avl_tree_remove_random_half);
+    tcase_add_test(tc, test_avl_tree_remove_half_with_duplicates);
+    return tc;
+}
+
+TCase *tcase_avl_tree_misc_create(void)
+{
+    TCase *tc = tcase_create("Miscellaneous");
+    tcase_add_checked_fixture(tc, setup, teardown);
+    tcase_add_test(tc, test_avl_tree_misc_traversal_forward);
+    tcase_add_test(tc, test_avl_tree_misc_traversal_backward);
+    tcase_add_test(tc, test_avl_tree_misc_key_changing);
+    return tc;
+}
+
+Suite *suite_avl_tree_create(void)
+{
+    Suite *suite = suite_create("AVL Tree");
+    suite_add_tcase(suite, tcase_avl_tree_insertion_only_create());
+    suite_add_tcase(suite, tcase_avl_tree_removal_create());
+    suite_add_tcase(suite, tcase_avl_tree_misc_create());
+    return suite;
+}
+

+ 31 - 0
tests/unit_tests/src/main.c

@@ -0,0 +1,31 @@
+/*
+ * Monolithium Unit Tests
+ * main.c
+ *
+ * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <check.h>
+
+Suite *suite_avl_tree_create(void);
+
+int main(int argc, char *argv[])
+{
+    SRunner *suite_runner = srunner_create(suite_avl_tree_create());
+    srunner_run_all(suite_runner, CK_NORMAL);
+    srunner_free(suite_runner);
+    return 0;
+}