#include #include #include #include #include #include #include namespace { std::array zeros() { return {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; } fesa::ResultDatabase valid_database() { fesa::NodalFrame nodal{ {fesa::NodeId{0}, fesa::NodeId{1}}, { zeros(), {1.0, 2.0, 3.0, 0.1, 0.2, 0.3}, }, { {-10.0, -20.0, -30.0, -1.0, -2.0, -3.0}, zeros(), }, }; fesa::ResultFrame frame{1.0, std::move(nodal), {}}; fesa::ResultStep step{"Load", {std::move(frame)}}; return {"1.0.0", {std::move(step)}}; } bool has_diagnostic( const fesa::Status& status, const std::string_view code) { return std::ranges::any_of( status.diagnostics, [code](const fesa::Diagnostic& diagnostic) { return diagnostic.code == code && diagnostic.stage == fesa::DiagnosticStage::results && diagnostic.severity == fesa::Severity::error; }); } TEST(NodalFrame, AcceptsFiniteSixComponentFieldsWithMatchingNodeIds) { const auto database = valid_database(); const auto status = fesa::validate_result_database(database); EXPECT_TRUE(status.succeeded); EXPECT_TRUE(status.diagnostics.empty()); } TEST(NodalFrame, RejectsDisplacementSizeMismatch) { auto database = valid_database(); database.steps[0].frames[0].nodal.displacement.pop_back(); const auto status = fesa::validate_result_database(database); EXPECT_FALSE(status.succeeded); EXPECT_TRUE(has_diagnostic(status, "results.nodal_size_mismatch")); } TEST(NodalFrame, RejectsReactionSizeMismatch) { auto database = valid_database(); database.steps[0].frames[0].nodal.reaction.pop_back(); const auto status = fesa::validate_result_database(database); EXPECT_FALSE(status.succeeded); EXPECT_TRUE(has_diagnostic(status, "results.nodal_size_mismatch")); } TEST(NodalFrame, RejectsDuplicateNodeId) { auto database = valid_database(); database.steps[0].frames[0].nodal.node_ids[1] = fesa::NodeId{0}; const auto status = fesa::validate_result_database(database); EXPECT_FALSE(status.succeeded); EXPECT_TRUE(has_diagnostic(status, "results.duplicate_node_id")); } TEST(NodalFrame, RejectsNonfiniteDisplacement) { auto database = valid_database(); database.steps[0].frames[0].nodal.displacement[1][2] = std::numeric_limits::quiet_NaN(); const auto status = fesa::validate_result_database(database); EXPECT_FALSE(status.succeeded); EXPECT_TRUE(has_diagnostic(status, "results.nonfinite_value")); } TEST(NodalFrame, RejectsNonfiniteReaction) { auto database = valid_database(); database.steps[0].frames[0].nodal.reaction[0][4] = std::numeric_limits::infinity(); const auto status = fesa::validate_result_database(database); EXPECT_FALSE(status.succeeded); EXPECT_TRUE(has_diagnostic(status, "results.nonfinite_value")); } TEST(ResultDatabase, RejectsDuplicateStepName) { auto database = valid_database(); database.steps.push_back(database.steps[0]); const auto status = fesa::validate_result_database(database); EXPECT_FALSE(status.succeeded); EXPECT_TRUE(has_diagnostic(status, "results.duplicate_step_name")); } TEST(ResultDatabase, RejectsDuplicateFrameTimeWithinStep) { auto database = valid_database(); database.steps[0].frames.push_back(database.steps[0].frames[0]); const auto status = fesa::validate_result_database(database); EXPECT_FALSE(status.succeeded); EXPECT_TRUE(has_diagnostic(status, "results.duplicate_frame_time")); } TEST(ResultDatabase, RejectsNonfiniteFrameTime) { auto database = valid_database(); database.steps[0].frames[0].step_time = std::numeric_limits::quiet_NaN(); const auto status = fesa::validate_result_database(database); EXPECT_FALSE(status.succeeded); EXPECT_TRUE(has_diagnostic(status, "results.nonfinite_value")); } } // namespace