diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-03-23 10:32:15 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-03-23 10:32:15 +0100 |
| commit | 48a2c33d205397adeaad385aebc1d1e008915b3e (patch) | |
| tree | 379265414d551747585a1f86b9f390539ef2dfeb | |
| parent | 754012dd458985a6a4953c99204c6651318892b2 (diff) | |
| download | teachos-48a2c33d205397adeaad385aebc1d1e008915b3e.tar.xz teachos-48a2c33d205397adeaad385aebc1d1e008915b3e.zip | |
ci: enable test builds
| -rw-r--r-- | .clang-tidy | 4 | ||||
| -rw-r--r-- | .clangd | 3 | ||||
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | .gitlab-ci.yml | 18 | ||||
| -rw-r--r-- | .lcovrc | 5 | ||||
| -rw-r--r-- | .vscode/settings.json | 16 | ||||
| -rw-r--r-- | CMakeLists.txt | 7 | ||||
| -rw-r--r-- | CMakePresets.json | 9 | ||||
| -rw-r--r-- | cmake/Modules/EnableCoverage.cmake | 9 | ||||
| -rw-r--r-- | libs/kstd/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | libs/kstd/tests/include/kstd/tests/os_panic.hpp | 23 | ||||
| -rw-r--r-- | libs/kstd/tests/os_mock.cpp | 15 | ||||
| -rw-r--r-- | libs/kstd/tests/src/os_panic.cpp | 15 | ||||
| -rw-r--r-- | libs/kstd/tests/src/vector.cpp | 395 | ||||
| -rw-r--r-- | libs/kstd/tests/vector.cpp | 11 |
15 files changed, 508 insertions, 39 deletions
diff --git a/.clang-tidy b/.clang-tidy index 71f1be9..8fa3943 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -67,4 +67,6 @@ CheckOptions: FormatStyle: file HeaderFilterRegex: '(.*/kstd/include/.*)|(arch|kernel|kapi)/.*\.hpp' -SystemHeaders: true
\ No newline at end of file +SystemHeaders: true +RemovedArgs: + - -fcondition-coverage
\ No newline at end of file @@ -1,6 +1,9 @@ Diagnostics: UnusedIncludes: Strict MissingIncludes: Strict +CompileFlags: + Remove: + - -fcondition-coverage --- If: @@ -9,5 +9,7 @@ /docs/_build qemu-*-*.log -
-/desktop.ini
+ +/desktop.ini + +coverage.info diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f2cfd1d..b37da93 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,6 +22,24 @@ build: - kernel.iso <<: *build_matrix +bht: + stage: build + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-3 + before_script: + - apt update + - apt install -y build-essential libcatch2-dev + script: + - cmake --preset bht + - cmake --build --preset bht-dbg + - ctest --preset bht-dbg + - lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info + - lcov --config-file .lcovrc --list coverage.info + coverage: '/Total:\|(\d+\.?\d+)\%/' + artifacts: + paths: + coverage.info + expire_in: 24 hours + license_check: stage: .pre image: @@ -0,0 +1,5 @@ +exclude = /usr/include/* +exclude = build/_deps/* +exclude = tests/* + +ignore_errors = unused,empty,inconsistent
\ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index f9b075d..21f4885 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,6 @@ "cmake.useCMakePresets": "always", "cmake.options.statusBarVisibility": "visible", "cmake.ctest.testExplorerIntegrationEnabled": false, - "clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/build", "--query-driver=**/x86_64-pc-elf-g++", @@ -11,16 +10,13 @@ "--clang-tidy", "--header-insertion=iwyu" ], - "files.associations": { "**/kstd/include/kstd/**": "cpp", }, - "[cpp]": { "editor.formatOnSave": true, "editor.tabSize": 2, }, - "[gas]": { "editor.rulers": [ 80 @@ -59,5 +55,15 @@ "teachos", "undelegated", "wrmsr" - ] + ], + "testMate.cpp.debug.configTemplate": { + "type": "cppdbg", + "MIMode": "gdb", + "program": "${exec}", + "args": "${argsArray}", + "cwd": "${cwd}", + "env": "${envObject}", + "environment": "${envObjArray}", + "sourceFileMap": "${sourceFileMapObj}" + } }
\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c3762..71e4fef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,9 @@ include("FetchContent") FetchContent_Declare( "Catch2" - GIT_REPOSITORY "https://github.com/catchorg/Catch2.git" - GIT_TAG "v3.13.0" + URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" + URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" + EXCLUDE_FROM_ALL FIND_PACKAGE_ARGS ) @@ -91,6 +92,8 @@ if(CMAKE_CROSSCOMPILING) add_subdirectory("kapi") add_subdirectory("libs") else() + include("EnableCoverage") + enable_testing() find_package("Catch2") include("Catch") diff --git a/CMakePresets.json b/CMakePresets.json index 8910b5f..3445119 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,6 +14,7 @@ }, { "name": "x86_64", + "description": "Target x86-64", "inherits": "base", "toolchainFile": "cmake/Platforms/x86_64.cmake" }, @@ -39,5 +40,11 @@ "configurePreset": "bht", "configuration": "Debug" } + ], + "testPresets": [ + { + "name": "bht-dbg", + "configurePreset": "bht" + } ] -} +}
\ No newline at end of file diff --git a/cmake/Modules/EnableCoverage.cmake b/cmake/Modules/EnableCoverage.cmake new file mode 100644 index 0000000..9602869 --- /dev/null +++ b/cmake/Modules/EnableCoverage.cmake @@ -0,0 +1,9 @@ +function (enable_coverage TARGET) + target_compile_options("${TARGET}" PRIVATE + "$<$<AND:$<CXX_COMPILER_ID:GNU,Clang>,$<CONFIG:Debug>>:-fcondition-coverage>" + "$<$<AND:$<CXX_COMPILER_ID:GNU,Clang>,$<CONFIG:Debug>>:--coverage>" + ) + target_link_libraries("${TARGET}" PRIVATE + "$<$<AND:$<CXX_COMPILER_ID:GNU,Clang>,$<CONFIG:Debug>>:gcov>" + ) +endfunction ()
\ No newline at end of file diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 2f360cd..06543ab 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -44,8 +44,12 @@ endif() if(NOT CMAKE_CROSSCOMPILING) add_executable("kstd_tests" - "tests/vector.cpp" - "tests/os_mock.cpp" + "tests/src/vector.cpp" + "tests/src/os_panic.cpp" + ) + + target_include_directories("kstd_tests" PRIVATE + "tests/include" ) target_link_libraries("kstd_tests" PRIVATE @@ -59,5 +63,8 @@ if(NOT CMAKE_CROSSCOMPILING) EXCLUDE_FROM_ALL NO ) + enable_coverage("kstd") + enable_coverage("kstd_tests") + catch_discover_tests("kstd_tests") endif()
\ No newline at end of file diff --git a/libs/kstd/tests/include/kstd/tests/os_panic.hpp b/libs/kstd/tests/include/kstd/tests/os_panic.hpp new file mode 100644 index 0000000..4396a9f --- /dev/null +++ b/libs/kstd/tests/include/kstd/tests/os_panic.hpp @@ -0,0 +1,23 @@ +#ifndef KSTD_TESTS_OS_PANIC_HPP +#define KSTD_TESTS_OS_PANIC_HPP + +#include <source_location> +#include <stdexcept> +#include <string> + +namespace kstd::tests +{ + + struct os_panic : std::runtime_error + { + os_panic(std::string message, std::source_location location) + : std::runtime_error{message} + , location(location) + {} + + std::source_location location; + }; + +} // namespace kstd::tests + +#endif
\ No newline at end of file diff --git a/libs/kstd/tests/os_mock.cpp b/libs/kstd/tests/os_mock.cpp deleted file mode 100644 index 39b7f0d..0000000 --- a/libs/kstd/tests/os_mock.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include <exception> -#include <format> -#include <source_location> -#include <stdexcept> -#include <string_view> - -namespace kstd::os -{ - auto panic(std::string_view message, std::source_location location) - { - auto full_message = - std::format("OS Panic Handler called '{}' at {}:{}", message, location.file_name(), location.line()); - throw std::runtime_error{full_message}; - } -} // namespace kstd::os
\ No newline at end of file diff --git a/libs/kstd/tests/src/os_panic.cpp b/libs/kstd/tests/src/os_panic.cpp new file mode 100644 index 0000000..3eae6ff --- /dev/null +++ b/libs/kstd/tests/src/os_panic.cpp @@ -0,0 +1,15 @@ +#include "kstd/tests/os_panic.hpp" + +#include <source_location> +#include <string> +#include <string_view> + +namespace kstd::os +{ + + auto panic(std::string_view message, std::source_location location) + { + throw kstd::tests::os_panic{std::string{message}, location}; + } + +} // namespace kstd::os
\ No newline at end of file diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp new file mode 100644 index 0000000..cb1b182 --- /dev/null +++ b/libs/kstd/tests/src/vector.cpp @@ -0,0 +1,395 @@ +#include "kstd/tests/os_panic.hpp" + +#include <kstd/ranges> +#include <kstd/vector> + +#include <catch2/catch_test_macros.hpp> + +#include <array> +#include <ranges> +#include <utility> + +SCENARIO("Vector initialization and construction", "[vector]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + kstd::vector<int> v; + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + } + + THEN("the size and capacity are zero") + { + REQUIRE(v.size() == 0); + REQUIRE(v.capacity() == 0); + } + } + + WHEN("constructing with a specific size") + { + kstd::vector<int> v(10); + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size is and capacity match the specified value") + { + REQUIRE(v.size() == 10); + REQUIRE(v.capacity() == 10); + } + } + + WHEN("constructing from an initializer list") + { + kstd::vector<int> v = {1, 2, 3, 4, 5}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size is and capacity match the specified value") + { + REQUIRE(v.size() == 5); + REQUIRE(v.capacity() == 5); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 4); + REQUIRE(v[4] == 5); + } + } + } + + GIVEN("A non-empty range") + { + auto range = std::array<int, 3>{1, 2, 3}; + + WHEN("constructing from a random-access iterator range") + { + auto v = kstd::vector<int>{std::begin(range), std::end(range)}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size and capacity match the range size") + { + REQUIRE(v.size() == std::size(range)); + REQUIRE(v.capacity() == std::size(range)); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("constructing from a range") + { + auto v = kstd::vector<int>{kstd::from_range, range}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size and capacity match the range size") + { + REQUIRE(v.size() == std::ranges::size(range)); + REQUIRE(v.capacity() == std::ranges::size(range)); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + } + + GIVEN("A populated vector") + { + kstd::vector<int> source = {1, 2, 3, 4, 5}; + + WHEN("copy constructing a new vector") + { + kstd::vector<int> copy(source); + + THEN("the copy matches the original") + { + REQUIRE(copy.size() == source.size()); + REQUIRE(copy.capacity() == source.capacity()); + REQUIRE(copy[0] == 1); + REQUIRE(copy[1] == 2); + REQUIRE(copy[2] == 3); + REQUIRE(copy[3] == 4); + REQUIRE(copy[4] == 5); + } + + THEN("the original is left unchanged") + { + REQUIRE(source.size() == 5); + REQUIRE(source.capacity() == 5); + REQUIRE(source[0] == 1); + REQUIRE(source[1] == 2); + REQUIRE(source[2] == 3); + REQUIRE(source[3] == 4); + REQUIRE(source[4] == 5); + } + } + + WHEN("move constructing a new vector") + { + kstd::vector<int> moved(std::move(source)); + + THEN("The new vector has the original elements") + { + REQUIRE(moved.size() == 5); + REQUIRE(moved.capacity() == 5); + REQUIRE(moved[0] == 1); + REQUIRE(moved[1] == 2); + REQUIRE(moved[2] == 3); + REQUIRE(moved[3] == 4); + REQUIRE(moved[4] == 5); + } + + THEN("The original vector is left in a valid but unspecified state") + { + REQUIRE(source.empty()); + REQUIRE(source.size() == 0); + REQUIRE(source.capacity() == 0); + } + } + } +} + +SCENARIO("Vector element access", "[vector]") +{ + GIVEN("A populated vector") + { + kstd::vector<int> v = {10, 20, 30}; + + WHEN("accessing elements for reading") + { + THEN("operator[] and at() return the correct elements") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + + REQUIRE(v.at(0) == 10); + REQUIRE(v.at(1) == 20); + REQUIRE(v.at(2) == 30); + } + + THEN("front() and back() return the first and last elements") + { + REQUIRE(v.front() == 10); + REQUIRE(v.back() == 30); + } + + THEN("data() return a pointer to the contiguous storage") + { + auto ptr = v.data(); + REQUIRE(ptr); + REQUIRE(ptr[0] == 10); + REQUIRE(ptr[1] == 20); + REQUIRE(ptr[2] == 30); + } + + THEN("accessing out of bounds elements panics") + { + REQUIRE_THROWS_AS(v.at(3), kstd::tests::os_panic); + } + } + + WHEN("accessing elements for writing") + { + v[0] = 100; + v.at(1) = 200; + v.back() = 300; + + THEN("the elements are correctly modified") + { + REQUIRE(v[0] == 100); + REQUIRE(v[1] == 200); + REQUIRE(v[2] == 300); + } + } + } +} + +SCENARIO("Vector iterators", "[vector]") +{ + GIVEN("A populated vector") + { + kstd::vector<int> v = {1, 2, 3}; + + WHEN("using forward iterators") + { + THEN("they navigate the elements in the correct forward order") + { + auto it = v.begin(); + REQUIRE(it != v.end()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it != v.end()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.end()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it == v.end()); + } + + THEN("const forward iterators provide correct access") + { + auto it = v.cbegin(); + REQUIRE(it != v.cend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it != v.cend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.cend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it == v.cend()); + } + } + + WHEN("using reverse iterators") + { + THEN("they navigate the elements in the correct reverse order") + { + auto it = v.rbegin(); + REQUIRE(it != v.rend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it != v.rend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.rend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it == v.rend()); + } + + THEN("const reverse iterators provide correct access") + { + auto it = v.crbegin(); + REQUIRE(it != v.crend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it != v.crend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.crend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it == v.crend()); + } + } + } + + GIVEN("an empty vector") + { + kstd::vector<int> v; + + WHEN("getting iterators") + { + THEN("begin() equals end() and cbegin() equals cend()") + { + REQUIRE(v.begin() == v.end()); + REQUIRE(v.cbegin() == v.cend()); + } + + THEN("rbegin() equals rend() and crbegin() equals crend()") + { + REQUIRE(v.rbegin() == v.rend()); + REQUIRE(v.crbegin() == v.crend()); + } + } + } +} + +SCENARIO("Vector capacity management", "[vector]") +{ + GIVEN("An empty vector") + { + kstd::vector<int> v; + + WHEN("reserving space") + { + v.reserve(10); + + THEN("the capacity is at least the reserved amount") + { + REQUIRE(v.capacity() >= 10); + } + + THEN("the size is still zero") + { + REQUIRE(v.size() == 0); + } + + THEN("the vector is still empty") + { + REQUIRE(v.empty()); + } + } + } + + GIVEN("A populated vector with excess capacity") + { + kstd::vector<int> v{1, 2, 3}; + v.reserve(10); + + REQUIRE(v.capacity() == 10); + + WHEN("calling shrink_to_fit") + { + v.shrink_to_fit(); + + THEN("the capacity is reduced to match the size") + { + REQUIRE(v.capacity() == 3); + REQUIRE(v.size() == 3); + } + + THEN("the elements remain unchanged") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + } +}
\ No newline at end of file diff --git a/libs/kstd/tests/vector.cpp b/libs/kstd/tests/vector.cpp deleted file mode 100644 index 3a45008..0000000 --- a/libs/kstd/tests/vector.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -TEST_CASE("Creating an empty vector") -{ - kstd::vector<int> v; - REQUIRE(v.empty()); - REQUIRE(v.size() == 0); - REQUIRE(v.capacity() == 0); -} |
