Files
MeanField_SERiF/tests/model/state.cpp
Emily Boudreaux cb20faeb3d 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.
2026-11-09 13:53:17 -05:00

94 lines
4.3 KiB
C++

#include <array>
#include <limits>
#include <utility>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include "serif/discretization/blocks/fields.hpp"
#include "serif/model/state.hpp"
namespace {
namespace blocks = serif::discretization::blocks;
namespace dependencies = serif::utils::dependencies;
using Density = blocks::density::mass;
using Shape = blocks::surface_deformation::coefficients;
using ShapeRow = blocks::surface_deformation::shape_equilibrium;
using Constant = blocks::barotropic_constant::mass_normalization;
using Layout = blocks::SystemLayout<dependencies::KeyList<Density, Shape, Constant>, dependencies::KeyList<ShapeRow, Constant, Density>>;
using State = serif::model::StellarState<Layout>;
}
TEST_CASE("A stellar state owns its layout and exposes fields in physical rather than residual order", "[model][state][unit]") {
Layout layout{.state_sizes = {2, 3, 1}, .residual_sizes = {3, 1, 2}};
State state(layout, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0});
layout.state_sizes[0] = 9;
layout.residual_sizes[0] = 8;
CHECK(state.layout().state_sizes == std::array<std::size_t, 3>{2, 3, 1});
CHECK(state.layout().residual_sizes == std::array<std::size_t, 3>{3, 1, 2});
CHECK(state.layout().offset<blocks::RepresentedValueKind::residual, Density>() == 4);
CHECK(state.get<Density>().data() == state.values().data());
CHECK(state.get<Shape>().data() == state.values().data() + 2);
CHECK(state.get<Constant>().data() == state.values().data() + 5);
REQUIRE(state.get<Shape>().size() == 3);
state.get<Shape>()[1] = 7.0;
state.get<Constant>()[0] = 8.0;
CHECK(state.values()[3] == 7.0);
CHECK(state.values()[5] == 8.0);
const State& read_only = state;
CHECK(read_only.get<Shape>()[1] == 7.0);
CHECK(read_only.values().data() == state.values().data());
}
TEST_CASE("Copied and moved stellar candidates retain independent coefficients and valid typed access", "[model][state][ownership]") {
const Layout layout{.state_sizes = {2, 3, 1}, .residual_sizes = {3, 1, 2}};
State original(layout, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0});
State candidate = original;
CHECK(candidate.values().data() != original.values().data());
candidate.get<Density>()[0] = 9.0;
candidate.get<Shape>()[2] = 10.0;
CHECK(original.get<Density>()[0] == 1.0);
CHECK(original.get<Shape>()[2] == 5.0);
State moved(std::move(candidate));
CHECK(moved.layout() == layout);
CHECK(moved.get<Density>()[0] == 9.0);
CHECK(moved.get<Shape>()[2] == 10.0);
CHECK(moved.get<Shape>().data() == moved.values().data() + 2);
moved.get<Constant>()[0] = 11.0;
CHECK(original.get<Constant>()[0] == 6.0);
State assigned(Layout{.state_sizes = {1, 1, 1}, .residual_sizes = {1, 1, 1}});
assigned = std::move(moved);
CHECK(assigned.layout() == layout);
CHECK(assigned.get<Constant>()[0] == 11.0);
CHECK(assigned.get<Constant>().data() == assigned.values().data() + 5);
assigned = original;
assigned.get<Density>()[1] = 12.0;
CHECK(original.get<Density>()[1] == 2.0);
}
TEST_CASE("Stellar states reject inconsistent storage and overflowing sizes while supporting empty coordinates", "[model][state][validation]") {
const Layout layout{.state_sizes = {2, 3, 1}, .residual_sizes = {3, 1, 2}};
const State zero(layout);
REQUIRE(zero.values().size() == 6);
for (double value : zero.values()) {
CHECK(value == 0.0);
}
CHECK_THROWS_WITH(State(layout, std::vector<double>(5)), Catch::Matchers::ContainsSubstring("INVALID_DOF_COUNT"));
CHECK_THROWS_WITH(State(layout, std::vector<double>(7)), Catch::Matchers::ContainsSubstring("INVALID_DOF_COUNT"));
const Layout overflow{.state_sizes = {std::numeric_limits<std::size_t>::max(), 1, 0}, .residual_sizes = {0, 1, 0}};
CHECK_THROWS_WITH(State(overflow), Catch::Matchers::ContainsSubstring("INVALID_DOF_COUNT"));
const State partial(Layout{.state_sizes = {0, 2, 0}, .residual_sizes = {2, 0, 0}});
CHECK(partial.get<Density>().empty());
CHECK(partial.get<Shape>().size() == 2);
CHECK(partial.get<Constant>().empty());
const blocks::SystemLayout<dependencies::KeyList<>, dependencies::KeyList<>> empty_layout{};
const serif::model::StellarState empty(empty_layout);
CHECK(empty.values().empty());
}