Files
FESADev/tests/integration/app/solid_application_test.cpp
T

175 lines
6.9 KiB
C++

#include <gtest/gtest.h>
#include <hdf5.h>
#include <fstream>
#include <iterator>
#include <map>
#include <stdexcept>
#include <vector>
#define NOMINMAX
#include <Windows.h>
#include "../../unit/io/hdf5/solid_output_test_fixture.h"
#include "fesa/app/fesa_application.h"
namespace {
std::map<std::string, std::vector<unsigned char>> NumericSnapshot(
const std::filesystem::path& path, unsigned p) {
std::vector<std::string> paths{"/model/nodal_constraint_mask"};
if (p == 2)
paths.insert(
paths.end(),
{"/steps/Step-1/eigen/modes", "/steps/Step-1/eigen/frequencies",
"/steps/Step-1/eigen/vectors",
"/steps/Step-1/eigen/verification/pairs",
"/steps/Step-1/eigen/verification/eta_gram",
"/steps/Step-1/eigen/verification/max_eta_orth"});
else {
if (p != 0) paths.push_back("/steps/Step-1/accepted_frames");
const unsigned count = p == 0 ? 1 : p == 1 ? 2 : 3;
for (unsigned i = 0; i < count; ++i) {
const auto root = "/steps/Step-1/frames/" + std::to_string(i);
for (const auto* field :
{"nodal/displacement", "nodal/reaction", "nodal/residual",
"solid/stress", "solid/strain", "global/strain_energy"})
paths.push_back(root + "/" + field);
if (p >= 3)
for (const auto* field :
{"nodal/velocity", "nodal/acceleration",
"nodal/algorithmic_acceleration", "global/kinetic_energy",
"global/external_work", "global/energy_balance_error",
"global/linear_momentum", "global/angular_momentum"})
paths.push_back(root + "/" + field);
}
}
const hid_t file =
H5Fopen(path.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
if (file < 0) throw std::runtime_error("CLI result file missing");
std::map<std::string, std::vector<unsigned char>> result;
for (const auto& dataset_path : paths) {
const hid_t dataset = H5Dopen2(file, dataset_path.c_str(), H5P_DEFAULT);
if (dataset < 0) {
H5Fclose(file);
throw std::runtime_error("CLI mandatory dataset missing");
}
const hid_t type = H5Dget_type(dataset), space = H5Dget_space(dataset);
const auto count = H5Sget_simple_extent_npoints(space);
std::vector<unsigned char> bytes(static_cast<std::size_t>(count) *
H5Tget_size(type));
const herr_t read = count == 0 ? 0
: H5Dread(dataset, type, H5S_ALL, H5S_ALL,
H5P_DEFAULT, bytes.data());
H5Sclose(space);
H5Tclose(type);
H5Dclose(dataset);
if (read < 0) {
H5Fclose(file);
throw std::runtime_error("CLI mandatory dataset read failed");
}
result.emplace(dataset_path, std::move(bytes));
}
H5Fclose(file);
return result;
}
TEST(SolidApplication, AllFiveActualProceduresWriteOfficialSolidSchema) {
for (unsigned p = 0; p < 5; ++p) {
SCOPED_TRACE(p);
solid_output_test::Files files(p);
ASSERT_EQ(fesa::FesaApplication{}.Run({files.input.string()}), 0);
const hid_t f =
H5Fopen(files.output.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
ASSERT_GE(f, 0);
const hid_t g = H5Gopen2(f, "/metadata", H5P_DEFAULT);
ASSERT_GE(g, 0);
const hid_t a = H5Aopen(g, "analysis_procedure", H5P_DEFAULT);
ASSERT_GE(a, 0);
const hid_t t = H5Aget_type(a);
char* value = nullptr;
ASSERT_GE(H5Aread(a, t, &value), 0);
EXPECT_STREQ(value, solid_output_test::Procedure(p));
H5free_memory(value);
H5Tclose(t);
H5Aclose(a);
H5Gclose(g);
H5Fclose(f);
}
}
TEST(SolidApplication,
NativeExecutableRunsAllFiveAndKeepsWarningsAndTypedLogs) {
std::vector<wchar_t> module(32768);
const DWORD length = GetModuleFileNameW(nullptr, module.data(),
static_cast<DWORD>(module.size()));
ASSERT_GT(length, 0u);
ASSERT_LT(length, module.size());
const auto executable =
std::filesystem::path(std::wstring(module.data(), length))
.parent_path()
.parent_path() /
"src/fesa/fesa.exe";
ASSERT_TRUE(std::filesystem::exists(executable));
for (unsigned p = 0; p < 5; ++p) {
SCOPED_TRACE(p);
solid_output_test::Files files(p);
ASSERT_EQ(fesa::FesaApplication{}.Run({files.input.string()}), 0);
const auto expected = NumericSnapshot(files.output, p);
std::wstring command =
L"\"" + executable.wstring() + L"\" \"" + files.input.wstring() + L"\"";
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
PROCESS_INFORMATION process{};
ASSERT_TRUE(CreateProcessW(executable.c_str(), command.data(), nullptr,
nullptr, FALSE, CREATE_NO_WINDOW, nullptr,
nullptr, &startup, &process));
const DWORD wait = WaitForSingleObject(process.hProcess, 60000);
if (wait != WAIT_OBJECT_0) TerminateProcess(process.hProcess, 99);
DWORD exit = 99;
EXPECT_TRUE(GetExitCodeProcess(process.hProcess, &exit));
CloseHandle(process.hThread);
CloseHandle(process.hProcess);
ASSERT_EQ(wait, WAIT_OBJECT_0);
ASSERT_EQ(exit, 0u);
EXPECT_GT(H5Fis_hdf5(files.output.string().c_str()), 0);
EXPECT_EQ(NumericSnapshot(files.output, p), expected);
std::ifstream stream(files.log, std::ios::binary);
std::string log{std::istreambuf_iterator<char>(stream), {}};
EXPECT_EQ(log.find("correction_rotation"), std::string::npos);
EXPECT_EQ(log.find("residual_moment"), std::string::npos);
if (p == 1)
EXPECT_NE(log.find("schema=solid-static-log-v1"), std::string::npos);
if (p >= 3) {
EXPECT_NE(log.find("schema=solid-hht-log-v1"), std::string::npos);
EXPECT_NE(log.find("h2_physical_da_over_L="), std::string::npos);
EXPECT_NE(log.find("h2_algorithmic_dA_over_L="), std::string::npos);
EXPECT_NE(log.find("normalized-high-low-physical-state;RN64-observables"),
std::string::npos);
}
const hid_t file =
H5Fopen(files.output.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
ASSERT_GE(file, 0);
const hid_t diagnostics = H5Dopen2(file, "/diagnostics", H5P_DEFAULT);
ASSERT_GE(diagnostics, 0);
const hid_t space = H5Dget_space(diagnostics);
hsize_t rows = 0;
H5Sget_simple_extent_dims(space, &rows, nullptr);
EXPECT_GT(rows, 0u);
H5Sclose(space);
H5Dclose(diagnostics);
H5Fclose(file);
}
}
TEST(SolidApplication, SuccessfulWarningsRetainSourceLocationAndTarget) {
solid_output_test::Files files(0);
testing::internal::CaptureStderr();
const int exit = fesa::FesaApplication{}.Run({files.input.string()});
const auto diagnostics = testing::internal::GetCapturedStderr();
ASSERT_EQ(exit, 0);
EXPECT_NE(diagnostics.find("code=inactive-solid-rotation-boundary"),
std::string::npos);
EXPECT_NE(diagnostics.find("entity_identity=Fixed"), std::string::npos);
EXPECT_NE(diagnostics.find("line=30"), std::string::npos);
EXPECT_NE(diagnostics.find(files.input.generic_u8string()),
std::string::npos);
}
} // namespace