aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy4
-rw-r--r--.clangd3
-rw-r--r--.gitignore6
-rw-r--r--.gitlab-ci.yml18
-rw-r--r--.lcovrc5
-rw-r--r--.vscode/settings.json16
-rw-r--r--CMakeLists.txt7
-rw-r--r--CMakePresets.json9
-rw-r--r--cmake/Modules/EnableCoverage.cmake9
-rw-r--r--libs/kstd/CMakeLists.txt11
-rw-r--r--libs/kstd/tests/include/kstd/tests/os_panic.hpp23
-rw-r--r--libs/kstd/tests/os_mock.cpp15
-rw-r--r--libs/kstd/tests/src/os_panic.cpp15
-rw-r--r--libs/kstd/tests/src/vector.cpp395
-rw-r--r--libs/kstd/tests/vector.cpp11
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
diff --git a/.clangd b/.clangd
index cb9a1f6..556b73c 100644
--- a/.clangd
+++ b/.clangd
@@ -1,6 +1,9 @@
Diagnostics:
UnusedIncludes: Strict
MissingIncludes: Strict
+CompileFlags:
+ Remove:
+ - -fcondition-coverage
---
If:
diff --git a/.gitignore b/.gitignore
index 21c6b0a..e96f481 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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:
diff --git a/.lcovrc b/.lcovrc
new file mode 100644
index 0000000..d19e80d
--- /dev/null
+++ b/.lcovrc
@@ -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);
-}