#include "fesa/io/abaqus/input_reader.h" #include #include #include #include #include #include #include namespace { class TemporaryInputFile { public: TemporaryInputFile(const std::string& stem, const std::string& content) : path_{std::filesystem::temp_directory_path() / ("fesa-" + stem + ".inp")} { std::ofstream stream{path_, std::ios::binary | std::ios::trunc}; stream.write(content.data(), static_cast(content.size())); if (!stream) { throw std::runtime_error{"Unable to create INP reader test fixture."}; } } ~TemporaryInputFile() { std::error_code error; std::filesystem::remove(path_, error); } const std::filesystem::path& Path() const noexcept { return path_; } private: std::filesystem::path path_; }; std::string ReadExactBytes(const std::filesystem::path& path) { std::ifstream stream{path, std::ios::binary}; if (!stream) { throw std::runtime_error{"Unable to read legacy INP fixture."}; } return std::string{std::istreambuf_iterator{stream}, std::istreambuf_iterator{}}; } std::filesystem::path RepositoryRoot() { auto path = std::filesystem::path{__FILE__}.parent_path(); for (int parent = 0; parent < 4; ++parent) { path = path.parent_path(); } return path; } } // namespace TEST(InpSyntax, RejectsMalformedOrOrphanData) { const auto missing_path = std::filesystem::temp_directory_path() / "fesa-missing-input.inp"; std::error_code remove_error; std::filesystem::remove(missing_path, remove_error); const auto unreadable = fesa::AbaqusInputReader{}.Read(missing_path); ASSERT_FALSE(unreadable.HasValue()); EXPECT_EQ(unreadable.GetStatus().Category(), fesa::FailureCategory::kInput); ASSERT_EQ(unreadable.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ(unreadable.GetStatus().Diagnostics()[0].code, "input-file-unreadable"); const TemporaryInputFile malformed{"malformed-keyword", "*, name=value\n"}; const auto malformed_result = fesa::AbaqusInputReader{}.Read(malformed.Path()); ASSERT_FALSE(malformed_result.HasValue()); EXPECT_EQ(malformed_result.GetStatus().Category(), fesa::FailureCategory::kInput); ASSERT_EQ(malformed_result.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ(malformed_result.GetStatus().Diagnostics()[0].code, "malformed-keyword"); EXPECT_EQ(malformed_result.GetStatus().Diagnostics()[0].location.line, 1U); const TemporaryInputFile orphan{"orphan-data", "** comment\n\norphan, data\n"}; const auto orphan_result = fesa::AbaqusInputReader{}.Read(orphan.Path()); ASSERT_FALSE(orphan_result.HasValue()); EXPECT_EQ(orphan_result.GetStatus().Category(), fesa::FailureCategory::kInput); ASSERT_EQ(orphan_result.GetStatus().Diagnostics().size(), 1U); EXPECT_EQ(orphan_result.GetStatus().Diagnostics()[0].code, "orphan-data-line"); EXPECT_EQ(orphan_result.GetStatus().Diagnostics()[0].location.line, 3U); } TEST(InpSyntax, ReadsLegacyCantileverWithoutMutation) { const auto input_path = RepositoryRoot() / "reference" / "cantilever beam" / "cantilever beam.inp"; const auto bytes_before = ReadExactBytes(input_path); const auto timestamp_before = std::filesystem::last_write_time(input_path); const auto result = fesa::AbaqusInputReader{}.Read(input_path); ASSERT_TRUE(result.HasValue()); EXPECT_EQ(result.Value().source_content_identity, "fnv1a64:04543464cc970405"); EXPECT_EQ(result.Value().source_path, std::filesystem::absolute(input_path).lexically_normal()); ASSERT_EQ(result.Value().blocks.size(), 30U); EXPECT_EQ(result.Value().blocks.front().canonical_name, "HEADING"); EXPECT_EQ(result.Value().blocks.front().location.line, 1U); EXPECT_EQ(result.Value().blocks.back().canonical_name, "END STEP"); const auto element = std::find_if(result.Value().blocks.begin(), result.Value().blocks.end(), [](const fesa::KeywordBlock& block) { return block.canonical_name == "ELEMENT"; }); ASSERT_NE(element, result.Value().blocks.end()); ASSERT_EQ(element->parameters.size(), 1U); EXPECT_EQ(element->parameters[0].name, "TYPE"); ASSERT_TRUE(element->parameters[0].value.has_value()); EXPECT_EQ(*element->parameters[0].value, "B33"); EXPECT_EQ(element->data.size(), 10U); EXPECT_EQ(ReadExactBytes(input_path), bytes_before); EXPECT_EQ(std::filesystem::last_write_time(input_path), timestamp_before); }