test(tests): added test suite

The test suite is extensive. Note that LLMs were used to aid in the development of the test suite. All LLM generated code was manually reviewed.
This commit is contained in:
2026-11-09 13:53:17 -05:00
parent 74e55a6859
commit cb20faeb3d
202 changed files with 35505 additions and 953 deletions

View File

@@ -0,0 +1,87 @@
#include <array>
#include <limits>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include "serif/solver/normalization/diagonal.hpp"
#include "serif/solver/normalization/point_rows.hpp"
namespace
{
namespace normalization = serif::solver::normalization;
}
// Ported from MeanField tests/normalization/physical_riesz.cpp, without assuming matching pressure and enthalpy scales.
TEST_CASE("Replaced residual rows use point metrics and independent physical units", "[normalization][unit][math]")
{
const std::array metric{4.0, 0.0, 16.0, 0.0};
const std::array point_rows{1, 3};
const auto enthalpy = normalization::make_point_replaced_residual_factors(metric, 5.0, point_rows, 5.0);
REQUIRE(enthalpy);
CHECK((*enthalpy)[0] == Catch::Approx(1.0 / 10.0));
CHECK((*enthalpy)[1] == Catch::Approx(1.0 / 5.0));
CHECK((*enthalpy)[2] == Catch::Approx(1.0 / 20.0));
CHECK((*enthalpy)[3] == Catch::Approx(1.0 / 5.0));
const auto pressure = normalization::make_point_replaced_residual_factors(metric, 5.0, point_rows, 7.0);
REQUIRE(pressure);
CHECK((*pressure)[0] == (*enthalpy)[0]);
CHECK((*pressure)[2] == (*enthalpy)[2]);
CHECK((*pressure)[1] == Catch::Approx(1.0 / 7.0));
CHECK((*pressure)[3] == Catch::Approx(1.0 / 7.0));
}
TEST_CASE("Point normalization rejects duplicate and out-of-range physical row selections", "[normalization][unit][validation]")
{
const std::array metric{4.0, 9.0};
const auto duplicate = normalization::make_point_replaced_residual_factors(metric, 1.0, std::array{1, 1}, 1.0);
REQUIRE_FALSE(duplicate);
CHECK_THAT(duplicate.error(), Catch::Matchers::ContainsSubstring("INVALID_ARGUMENT"));
for (const int row : {-1, 2})
{
const auto outside = normalization::make_point_replaced_residual_factors(metric, 1.0, std::array{row}, 1.0);
REQUIRE_FALSE(outside);
CHECK_THAT(outside.error(), Catch::Matchers::ContainsSubstring("OUT_OF_RANGE_ERROR"));
}
CHECK_FALSE(normalization::make_point_replaced_residual_factors({}, 1.0, std::array{0}, 1.0));
}
TEST_CASE("Only replaced residual rows may have unusable volume metrics", "[normalization][unit][validation]")
{
for (const double invalid : {0.0, -1.0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::infinity()})
{
const std::array metric{4.0, invalid};
const auto replaced = normalization::make_point_replaced_residual_factors(metric, 2.0, std::array{1}, 5.0);
REQUIRE(replaced);
CHECK((*replaced)[0] == Catch::Approx(0.25));
CHECK((*replaced)[1] == Catch::Approx(0.2));
CHECK_FALSE(normalization::make_point_replaced_residual_factors(metric, 2.0, {}, 5.0));
CHECK_FALSE(normalization::make_point_replaced_residual_factors(metric, invalid, std::array{1}, 5.0));
CHECK_FALSE(normalization::make_point_replaced_residual_factors(metric, 2.0, std::array{1}, invalid));
}
}
TEST_CASE("Point row composition validates factors only in the representations actually used", "[normalization][unit][math]")
{
const auto extreme_bulk = normalization::make_point_replaced_residual_factors(std::array{1.0e300, 0.0}, 1.0e-310, std::array{1}, 2.0);
REQUIRE(extreme_bulk);
CHECK((*extreme_bulk)[0] == Catch::Approx(1.0e160).epsilon(1.0e-12));
CHECK((*extreme_bulk)[1] == 0.5);
const auto no_points = normalization::make_point_replaced_residual_factors(std::array{4.0}, 2.0, {}, 1.0e-310);
REQUIRE(no_points);
CHECK(no_points->front() == 0.25);
CHECK_FALSE(normalization::make_point_replaced_residual_factors(std::array{4.0}, 2.0, std::array{0}, 1.0e-310));
const auto no_bulk = normalization::make_point_replaced_residual_factors(std::array{0.0}, 1.0e-310, std::array{0}, 2.0);
REQUIRE(no_bulk);
CHECK(no_bulk->front() == 0.5);
const auto empty = normalization::make_point_replaced_residual_factors({}, 1.0e-310, {}, 1.0e-310);
REQUIRE(empty);
CHECK(empty->empty());
const auto empty_factors = normalization::make_riesz_factors(serif::discretization::blocks::RepresentedValueKind::residual, {}, 1.0e-310);
REQUIRE(empty_factors);
CHECK(empty_factors->empty());
}