diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_matrix.cpp | 96 | ||||
| -rw-r--r-- | tests/test_vector.cpp | 88 |
2 files changed, 184 insertions, 0 deletions
diff --git a/tests/test_matrix.cpp b/tests/test_matrix.cpp new file mode 100644 index 0000000..b5fb60a --- /dev/null +++ b/tests/test_matrix.cpp @@ -0,0 +1,96 @@ +#include "matrix.hpp" + +#include <catch2/catch_test_macros.hpp> + +#include <type_traits> +#include <utility> + +using linalg::Matrix; + +TEST_CASE("Matrix constructors initialize dimensions and values", "[matrix]") { + const Matrix empty; + CHECK(empty.rows() == 0); + CHECK(empty.cols() == 0); + CHECK(empty.empty()); + + const Matrix zeroed(2, 3); + REQUIRE(zeroed.rows() == 2); + REQUIRE(zeroed.cols() == 3); + for (std::size_t i = 0; i < zeroed.rows(); ++i) { + for (std::size_t j = 0; j < zeroed.cols(); ++j) { + CHECK(zeroed(i, j) == 0.0); + } + } + + const Matrix filled(2, 2, 1.5); + CHECK(filled(0, 0) == 1.5); + CHECK(filled(0, 1) == 1.5); + CHECK(filled(1, 0) == 1.5); + CHECK(filled(1, 1) == 1.5); +} + +TEST_CASE("Matrix supports checked element access and fill", "[matrix]") { + Matrix a(2, 3); + a(0, 0) = 1.0; + a(0, 1) = 2.0; + a(1, 2) = 5.0; + + CHECK(a(0, 0) == 1.0); + CHECK(a(0, 1) == 2.0); + CHECK(a(1, 2) == 5.0); + + a.fill(-3.0); + for (std::size_t i = 0; i < a.rows(); ++i) { + for (std::size_t j = 0; j < a.cols(); ++j) { + CHECK(a(i, j) == -3.0); + } + } + + CHECK_THROWS_AS(a(2, 0), std::out_of_range); + CHECK_THROWS_AS(a(0, 3), std::out_of_range); +} + +TEST_CASE("Matrix identity and zero factories work", "[matrix]") { + const Matrix identity = Matrix::identity(3); + REQUIRE(identity.rows() == 3); + REQUIRE(identity.cols() == 3); + + for (std::size_t i = 0; i < identity.rows(); ++i) { + for (std::size_t j = 0; j < identity.cols(); ++j) { + const double expected = (i == j) ? 1.0 : 0.0; + CHECK(identity(i, j) == expected); + } + } + + const Matrix zeros = Matrix::zeros(2, 4); + CHECK(zeros.rows() == 2); + CHECK(zeros.cols() == 4); + for (std::size_t i = 0; i < zeros.rows(); ++i) { + for (std::size_t j = 0; j < zeros.cols(); ++j) { + CHECK(zeros(i, j) == 0.0); + } + } +} + +TEST_CASE("Matrix copy, move, and initializer list keeps row-major layout", "[matrix]") { + static_assert(std::is_nothrow_move_constructible_v<Matrix>); + + Matrix original{{1.0, 2.0}, {3.0, 4.0}}; + Matrix copied = original; + copied(0, 0) = 10.0; + + CHECK(original(0, 0) == 1.0); + CHECK(copied(0, 0) == 10.0); + + Matrix moved = std::move(original); + REQUIRE(moved.rows() == 2); + REQUIRE(moved.cols() == 2); + CHECK(moved(0, 0) == 1.0); + CHECK(moved(0, 1) == 2.0); + CHECK(moved(1, 0) == 3.0); + CHECK(moved(1, 1) == 4.0); +} + +TEST_CASE("Matrix initializer list rejects unequal row lengths", "[matrix]") { + CHECK_THROWS_AS((Matrix{{1.0, 2.0}, {3.0}}), std::invalid_argument); +} diff --git a/tests/test_vector.cpp b/tests/test_vector.cpp new file mode 100644 index 0000000..1d7b91a --- /dev/null +++ b/tests/test_vector.cpp @@ -0,0 +1,88 @@ +#include "linalg_error.hpp" +#include "vector.hpp" + +#include <catch2/catch_test_macros.hpp> + +#include <type_traits> +#include <utility> + +using linalg::DimensionMismatchError; +using linalg::Vector; + +TEST_CASE("Vector constructors initialize size and values", "[vector]") { + const Vector empty; + REQUIRE(empty.size() == 0); + REQUIRE(empty.empty()); + + const Vector zeroed(4); + REQUIRE(zeroed.size() == 4); + for (std::size_t i = 0; i < zeroed.size(); ++i) { + CHECK(zeroed[i] == 0.0); + } + + const Vector filled(3, 2.5); + REQUIRE(filled.size() == 3); + CHECK(filled[0] == 2.5); + CHECK(filled[1] == 2.5); + CHECK(filled[2] == 2.5); +} + +TEST_CASE("Vector supports checked element access and fill", "[vector]") { + Vector v{1.0, 2.0, 3.0}; + REQUIRE(v.size() == 3); + + v[1] = 7.0; + CHECK(v[0] == 1.0); + CHECK(v[1] == 7.0); + CHECK(v[2] == 3.0); + + v.fill(-2.0); + CHECK(v[0] == -2.0); + CHECK(v[1] == -2.0); + CHECK(v[2] == -2.0); + + CHECK_THROWS_AS(v[3], std::out_of_range); +} + +TEST_CASE("Vector copy and move preserve values", "[vector]") { + static_assert(std::is_nothrow_move_constructible_v<Vector>); + + Vector original{4.0, -1.0, 8.0}; + Vector copied = original; + copied[0] = 10.0; + + CHECK(original[0] == 4.0); + CHECK(copied[0] == 10.0); + + Vector moved = std::move(original); + REQUIRE(moved.size() == 3); + CHECK(moved[0] == 4.0); + CHECK(moved[1] == -1.0); + CHECK(moved[2] == 8.0); +} + +TEST_CASE("Vector arithmetic enforces shape compatibility", "[vector]") { + const Vector a{1.0, 2.0, 3.0}; + const Vector b{4.0, 5.0, 6.0}; + + const Vector sum = a + b; + CHECK(sum[0] == 5.0); + CHECK(sum[1] == 7.0); + CHECK(sum[2] == 9.0); + + const Vector diff = b - a; + CHECK(diff[0] == 3.0); + CHECK(diff[1] == 3.0); + CHECK(diff[2] == 3.0); + + const Vector scaled = 0.5 * a; + CHECK(scaled[0] == 0.5); + CHECK(scaled[1] == 1.0); + CHECK(scaled[2] == 1.5); + + CHECK(linalg::dot(a, b) == 32.0); + + const Vector short_vec{1.0, 2.0}; + CHECK_THROWS_AS(a + short_vec, DimensionMismatchError); + CHECK_THROWS_AS(linalg::dot(a, short_vec), DimensionMismatchError); +} |