175 lines
6.9 KiB
C++
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
|