#include #include #include #include #include #include #include "serif/utils/laneemden/solution.hpp" namespace { namespace laneemden = serif::utils::laneemden; constexpr laneemden::Sample sample{.xi = 2.0, .theta = 0.5, .derivative = -0.25}; /** @brief Use distinct profile and surface values so selection forwarding is observable. */ [[nodiscard]] laneemden::Solution example_solution() { return { .n = 1.0, .samples = {{.xi = 0.0, .theta = 1.0, .derivative = 0.0}, sample}, .surface = laneemden::Sample{.xi = 3.0, .theta = 0.0, .derivative = -0.5} }; } } TEST_CASE("Lane Emden sample formatting selects complete or individual values", "[laneemden][formatting]") { CHECK(std::format("{}", sample) == "(ξ=2, θ=0.5, dθ/dξ=-0.25)"); CHECK(std::format("{:}", sample) == std::format("{}", sample)); CHECK(std::format("{:all}", sample) == std::format("{}", sample)); CHECK(std::format("{:xi}", sample) == "2"); CHECK(std::format("{:theta}", sample) == "0.5"); CHECK(std::format("{:derivative}", sample) == "-0.25"); CHECK(std::format("{1:theta} / {0:xi}", sample, sample) == "0.5 / 2"); CHECK(std::format("{{{:xi}}}", sample) == "{2}"); } TEST_CASE("Lane Emden solution formatting forwards selection to its profile and surface", "[laneemden][formatting]") { const auto solution = example_solution(); CHECK(std::format("{}", solution) == "Solution(n=1, samples=[(ξ=0, θ=1, dθ/dξ=0), (ξ=2, θ=0.5, dθ/dξ=-0.25)], surface=(ξ=3, θ=0, dθ/dξ=-0.5))"); CHECK(std::format("{:}", solution) == std::format("{}", solution)); CHECK(std::format("{:all}", solution) == std::format("{}", solution)); CHECK(std::format("{:xi}", solution) == "Solution(n=1, samples=[0, 2], surface=3)"); CHECK(std::format("{:theta}", solution) == "Solution(n=1, samples=[1, 0.5], surface=0)"); CHECK(std::format("{:derivative}", solution) == "Solution(n=1, samples=[0, -0.25], surface=-0.5)"); const laneemden::Solution empty{.n = 5.0, .samples = {}, .surface = {}}; CHECK(std::format("{}", empty) == "Solution(n=5, samples=[], surface=none)"); CHECK(std::format("{:theta}", empty) == "Solution(n=5, samples=[], surface=none)"); } TEST_CASE("Lane Emden formatters preserve surrounding output and independent selectors", "[laneemden][formatting]") { const auto solution = example_solution(); const std::string expected = "prefix Solution(n=1, samples=[1, 0.5], surface=0) | 2 | Solution(n=1, samples=[0, -0.25], surface=-0.5) suffix"; std::string appended = "prefix "; std::format_to(std::back_inserter(appended), "{:theta} | {:xi} | {:derivative} suffix", solution, sample, solution); CHECK(appended == expected); std::array buffer{}; const auto end = std::format_to(buffer.data(), "prefix {:theta} | {:xi} | {:derivative} suffix", solution, sample, solution); CHECK(std::string(buffer.data(), end) == expected); } TEST_CASE("Lane Emden formatters reject unsupported selectors through the central error helper", "[laneemden][formatting]") { const auto solution = example_solution(); for (const std::string specifier : {"radius", "xi,theta", "theta ", ".2f", "theta:.2f"}) { CAPTURE(specifier); const std::string format = "{:" + specifier + "}"; CHECK_THROWS_AS(std::vformat(format, std::make_format_args(sample)), std::format_error); CHECK_THROWS_AS(std::vformat(format, std::make_format_args(solution)), std::format_error); CHECK_THROWS_WITH(std::vformat(format, std::make_format_args(sample)), Catch::Matchers::ContainsSubstring("Unknown Lane–Emden sample selector")); } CHECK_THROWS_AS(std::vformat("{:xi", std::make_format_args(sample)), std::format_error); CHECK_THROWS_AS(std::vformat("{:theta", std::make_format_args(solution)), std::format_error); }