aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2026-02-19 13:30:45 +0100
committerFelix Morgner <felix.morgner@gmail.com>2026-02-19 13:30:45 +0100
commite6d59a7b506f610b82340b62457f9dd9ad0cb53d (patch)
treeb83affc4789146822a9fdbda88402b87c5f0fa28
parente34239cf2bdb6bd810826b82c2a66873bd0881e2 (diff)
downloadsnake.s-e6d59a7b506f610b82340b62457f9dd9ad0cb53d.tar.xz
snake.s-e6d59a7b506f610b82340b62457f9dd9ad0cb53d.zip
feat: integrate basic CMocka tests
-rw-r--r--.vscode/settings.json9
-rw-r--r--CMakeLists.txt20
-rw-r--r--CMakePresets.json3
-rw-r--r--libs/SDL/error.S6
-rw-r--r--libs/SDL/events.S6
-rw-r--r--libs/SDL/init.S6
-rw-r--r--libs/SDL/rect.S6
-rw-r--r--libs/SDL/render.S6
-rw-r--r--libs/SDL/video.S4
-rw-r--r--src/error.S3
-rw-r--r--src/helpers/function.S4
-rw-r--r--src/snake.S77
-rw-r--r--tests/CMakeLists.txt42
-rw-r--r--tests/main.c5
-rw-r--r--tests/snake.c65
-rw-r--r--tests/suites.h6
16 files changed, 259 insertions, 9 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..e2ce90a
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,9 @@
+{
+ "cSpell.words": [
+ "ifndef",
+ "Lexit",
+ "offsetof",
+ "stdc",
+ "stddef"
+ ]
+} \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e2baef..7f97d30 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,15 +6,29 @@ project("snake.s"
LANGUAGES ASM
)
+option(SNAKE_BUILD_TESTS "Build the snake.s tests" ON)
+
add_subdirectory("libs")
-add_executable("snake.s"
+add_library("snake.core"
"src/error.S"
- "src/main.S"
+ "src/snake.S"
+)
+
+target_include_directories("snake.core" PUBLIC
+ "src"
)
-target_link_libraries("snake.s" PRIVATE
+target_link_libraries("snake.core" PUBLIC
"bindings::SDL2"
)
+add_executable("snake.s" "src/main.S")
+
+target_link_libraries("snake.s" PRIVATE "snake.core")
+
install(TARGETS "snake.s")
+
+if(SNAKE_BUILD_TESTS)
+ add_subdirectory("tests")
+endif() \ No newline at end of file
diff --git a/CMakePresets.json b/CMakePresets.json
index dc09fc2..e646f4a 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -7,7 +7,8 @@
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
- "CMAKE_BUILD_TYPE": "Debug"
+ "CMAKE_BUILD_TYPE": "Debug",
+ "CMAKE_EXPORT_COMPILE_COMMANDS": true
}
}
],
diff --git a/libs/SDL/error.S b/libs/SDL/error.S
index 19efb5a..ea2d853 100644
--- a/libs/SDL/error.S
+++ b/libs/SDL/error.S
@@ -1,3 +1,5 @@
+#ifndef __STDC_VERSION__
+
//! @file error.S
//!
//! Assembler bindings for SDL2 (SDL_error.h)
@@ -5,4 +7,6 @@
//! @fn char const * SDL_GetError(void)
//! @return the last error message
.type SDL_GetError, @function
-.extern SDL_GetError \ No newline at end of file
+.extern SDL_GetError
+
+#endif \ No newline at end of file
diff --git a/libs/SDL/events.S b/libs/SDL/events.S
index b94b65c..ce3a767 100644
--- a/libs/SDL/events.S
+++ b/libs/SDL/events.S
@@ -1,3 +1,5 @@
+#ifndef __STDC_VERSION__
+
//! @file events.S
//!
//! Assembler bindings for SDL2 (SDL_events.h)
@@ -12,4 +14,6 @@
//! @param event the SDL_Event structure to be filled with the next event from the queue, or NULL.
//! @return 1 if there is a pending event, or 0 if there isn't.
.type SDL_PollEvent, @function
-.extern SDL_PollEvent \ No newline at end of file
+.extern SDL_PollEvent
+
+#endif \ No newline at end of file
diff --git a/libs/SDL/init.S b/libs/SDL/init.S
index a2880c8..7026d64 100644
--- a/libs/SDL/init.S
+++ b/libs/SDL/init.S
@@ -1,3 +1,5 @@
+#ifndef __STDC_VERSION__
+
//! @file init.S
//!
//! Assembler bindings for SDL2 (SDL.h)
@@ -28,4 +30,6 @@
//! @fn void SDL_Quit(void)
.type SDL_Quit, @function
-.extern SDL_Quit \ No newline at end of file
+.extern SDL_Quit
+
+#endif \ No newline at end of file
diff --git a/libs/SDL/rect.S b/libs/SDL/rect.S
index f402324..a535f3b 100644
--- a/libs/SDL/rect.S
+++ b/libs/SDL/rect.S
@@ -1,3 +1,5 @@
+#ifndef __STDC_VERSION__
+
//! @file rect.S
//!
//! Assembler bindings for SDL2 (SDL_rect.h)
@@ -7,4 +9,6 @@
#define OFFSET_SDL_Rect_w 8
#define OFFSET_SDL_Rect_h 12
-#define SIZE_SDL_Rect 16 \ No newline at end of file
+#define SIZE_SDL_Rect 16
+
+#endif \ No newline at end of file
diff --git a/libs/SDL/render.S b/libs/SDL/render.S
index 61c06e5..d256bff 100644
--- a/libs/SDL/render.S
+++ b/libs/SDL/render.S
@@ -1,3 +1,5 @@
+#ifndef __STDC_VERSION__
+
//! @file render.S
//!
//! Assembler bindings for SDL2 (SDL_render.h)
@@ -45,4 +47,6 @@
//! @param a the alpha value used to draw on the rendering target.
//! @return 0 on success, or a negative value on error.
.type SDL_SetRenderDrawColor, @function
-.extern SDL_SetRenderDrawColor \ No newline at end of file
+.extern SDL_SetRenderDrawColor
+
+#endif \ No newline at end of file
diff --git a/libs/SDL/video.S b/libs/SDL/video.S
index f91cc89..78623bd 100644
--- a/libs/SDL/video.S
+++ b/libs/SDL/video.S
@@ -1,3 +1,5 @@
+#ifndef __STDC_VERSION__
+
//! @file video.S
//!
//! Assembler bindings for SDL2 (SDL_video.h)
@@ -21,3 +23,5 @@
//! @param window the window to destroy
.type SDL_DestroyWindow, @function
.extern SDL_DestroyWindow
+
+#endif \ No newline at end of file
diff --git a/src/error.S b/src/error.S
index 44be8b0..50597dd 100644
--- a/src/error.S
+++ b/src/error.S
@@ -1,6 +1,8 @@
#include "helpers/function.S"
#include "SDL/error.S"
+#ifndef __STDC_VERSION__
+
.section .text
//! @function _print_sdl_error(char const * format)
@@ -19,3 +21,4 @@
function_end
+#endif \ No newline at end of file
diff --git a/src/helpers/function.S b/src/helpers/function.S
index af41905..eeada7c 100644
--- a/src/helpers/function.S
+++ b/src/helpers/function.S
@@ -1,3 +1,5 @@
+#ifndef __STDC_VERSION__
+
.set .L_FUNCTION_IS_IN_FUNCTION_DEFINITION, 0
.set .L_FUNCTION_LOCALS_ALLOCATED, 0
@@ -122,3 +124,5 @@
.cfi_def_cfa_register %rbp
.set .L\name\()_FRAME_OFFSET, 0
.endm
+
+#endif \ No newline at end of file
diff --git a/src/snake.S b/src/snake.S
new file mode 100644
index 0000000..00d66e2
--- /dev/null
+++ b/src/snake.S
@@ -0,0 +1,77 @@
+//! @file snake.S
+//!
+//! Type definitions, sizes, and the interface for the snake.
+
+#include "helpers/function.S"
+
+#define OFFSET_snake_t_x 0
+#define SIZE_snake_t_x 4
+
+#define OFFSET_snake_t_y OFFSET_snake_t_x + SIZE_snake_t_x
+#define SIZE_snake_t_y 4
+
+#define OFFSET_snake_t_length OFFSET_snake_t_y + SIZE_snake_t_y
+#define SIZE_snake_t_length 4
+
+#define OFFSET_snake_t_direction OFFSET_snake_t_length + SIZE_snake_t_length
+#define SIZE_snake_t_direction 1
+
+#define OFFSET_snake_t_padding OFFSET_snake_t_direction + SIZE_snake_t_direction
+#define SIZE_snake_t_padding 3
+
+#define SIZE_snake_t OFFSET_snake_t_padding + SIZE_snake_t_padding
+
+#ifndef __STDC_VERSION__
+
+.section .text
+
+ //! @fn void snake_init(snake_t * self, int x, int y, int length)
+ //! @param (%rdi) self the object to initialize
+ //! @param (%esi) x the x position of the head of the snake
+ //! @param (%edx) y the y position of the head of the snake
+ //! @param (%ecx) length the length of the snake
+ function_begin snake_init
+
+ test %rdi, %rdi
+ jz .Lexit_snake_init
+
+ mov %esi, OFFSET_snake_t_x(%rdi)
+ mov %edx, OFFSET_snake_t_y(%rdi)
+ mov %ecx, OFFSET_snake_t_length(%rdi)
+ xor %al, %al
+ mov %al, OFFSET_snake_t_direction(%rdi)
+
+ function_end
+
+#else
+
+#include <stddef.h>
+
+typedef struct {
+ int x;
+ int y;
+ int length;
+ char direction;
+ char padding[3];
+} snake_t;
+
+static_assert(sizeof(snake_t) == SIZE_snake_t);
+
+static_assert(offsetof(snake_t, x) == OFFSET_snake_t_x);
+static_assert(sizeof(((snake_t*)0)->x) == SIZE_snake_t_x);
+
+static_assert(offsetof(snake_t, y) == OFFSET_snake_t_y);
+static_assert(sizeof(((snake_t*)0)->y) == SIZE_snake_t_y);
+
+static_assert(offsetof(snake_t, length) == OFFSET_snake_t_length);
+static_assert(sizeof(((snake_t*)0)->length) == SIZE_snake_t_length);
+
+static_assert(offsetof(snake_t, direction) == OFFSET_snake_t_direction);
+static_assert(sizeof(((snake_t*)0)->direction) == SIZE_snake_t_direction);
+
+static_assert(offsetof(snake_t, padding) == OFFSET_snake_t_padding);
+static_assert(sizeof(((snake_t*)0)->padding) == SIZE_snake_t_padding);
+
+void snake_init(snake_t * self, int x, int y, int length);
+
+#endif \ No newline at end of file
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..3ed41d4
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,42 @@
+enable_language(C)
+
+set(CMAKE_C_STANDARD 23)
+set(CMAKE_C_STANDARD_REQUIRED YES)
+set(CMAKE_C_EXTENSIONS OFF)
+
+include("FetchContent")
+
+FetchContent_Declare("CMocka"
+ URL "https://gitlab.com/cmocka/cmocka/-/archive/cmocka-2.0.0/cmocka-cmocka-2.0.0.zip"
+ URL_HASH "SHA256=c2a53cc0a45e8be734f657e48daa687f077def759ea30adcd46bdb842a8fb269"
+)
+
+set(BUILD_SHARED_LIBS OFF)
+set(WITH_EXAMPLES OFF)
+
+FetchContent_MakeAvailable("CMocka")
+
+enable_testing()
+
+add_executable("snake.tests"
+ "main.c"
+ "snake.c"
+)
+
+target_compile_features("snake.tests" PUBLIC
+ "c_std_23"
+)
+
+target_compile_options("snake.tests" PUBLIC
+ "-Wall"
+ "-Wextra"
+ "-Werror"
+ "-pedantic-errors"
+)
+
+target_link_libraries("snake.tests" PUBLIC
+ "snake.core"
+ "cmocka"
+)
+
+add_test(NAME "snake.tests" COMMAND "snake.tests") \ No newline at end of file
diff --git a/tests/main.c b/tests/main.c
new file mode 100644
index 0000000..e156b31
--- /dev/null
+++ b/tests/main.c
@@ -0,0 +1,5 @@
+#include "suites.h"
+
+#include <cmocka.h>
+
+int main() { return run_snake_tests(); } \ No newline at end of file
diff --git a/tests/snake.c b/tests/snake.c
new file mode 100644
index 0000000..f4acc7e
--- /dev/null
+++ b/tests/snake.c
@@ -0,0 +1,65 @@
+#include "suites.h"
+
+#include "snake.S"
+
+#include <cmocka.h>
+
+
+
+extern void snake_init(snake_t *self, int x, int y, int length);
+
+static void test_snake_init_sets_x([[maybe_unused]] void **state) {
+ // Arrange:
+ snake_t snake;
+
+ // Act:
+ snake_init(&snake, 1, 2, 3);
+
+ // Assert:
+ assert_int_equal(snake.x, 1);
+}
+
+static void test_snake_init_sets_y([[maybe_unused]] void **state) {
+ // Arrange:
+ snake_t snake;
+
+ // Act:
+ snake_init(&snake, 1, 2, 3);
+
+ // Assert:
+ assert_int_equal(snake.y, 2);
+}
+
+static void test_snake_init_sets_length([[maybe_unused]] void **state) {
+ // Arrange:
+ snake_t snake;
+
+ // Act:
+ snake_init(&snake, 1, 2, 3);
+
+ // Assert:
+ assert_int_equal(snake.length, 3);
+}
+
+static void
+test_snake_init_sets_direction_to_zero([[maybe_unused]] void **state) {
+ // Arrange:
+ snake_t snake;
+
+ // Act:
+ snake_init(&snake, 1, 2, 3);
+
+ // Assert:
+ assert_int_equal(snake.direction, 0);
+}
+
+int run_snake_tests(void) {
+ struct CMUnitTest const tests[] = {
+ cmocka_unit_test(test_snake_init_sets_x),
+ cmocka_unit_test(test_snake_init_sets_y),
+ cmocka_unit_test(test_snake_init_sets_length),
+ cmocka_unit_test(test_snake_init_sets_direction_to_zero),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/suites.h b/tests/suites.h
new file mode 100644
index 0000000..ea82ece
--- /dev/null
+++ b/tests/suites.h
@@ -0,0 +1,6 @@
+#ifndef SNAKE_TESTS_SUITES_H
+#define SNAKE_TESTS_SUITES_H
+
+int run_snake_tests();
+
+#endif \ No newline at end of file