feat(nonlinear-static-newton-raphson): step 0 - cli-output-lease-and-derived-names
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include "fesa/app/fesa_application.h"
|
||||
#include "fesa/app/output_paths.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hdf5.h>
|
||||
|
||||
@@ -9,6 +11,7 @@
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -332,32 +335,37 @@ void ExpectDiagnosticFieldOrder(const std::string& text) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ExplicitOutputArguments(
|
||||
const std::filesystem::path& input, const std::filesystem::path& output) {
|
||||
// FesaApplication receives argv[0]-excluded operands and options.
|
||||
return {input.string(), "--output", output.string()};
|
||||
struct FileSnapshot {
|
||||
std::string contents;
|
||||
std::filesystem::file_time_type write_time;
|
||||
};
|
||||
|
||||
FileSnapshot SnapshotFile(const std::filesystem::path& path) {
|
||||
std::ifstream stream{path, std::ios::binary};
|
||||
if (!stream) {
|
||||
throw std::runtime_error{"Unable to open an app output fixture."};
|
||||
}
|
||||
return {{std::istreambuf_iterator<char>{stream},
|
||||
std::istreambuf_iterator<char>{}},
|
||||
std::filesystem::last_write_time(path)};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(LinearStaticCli, DefaultAndExplicitOutputProduceFesaHdf5) {
|
||||
// NSNR-CLI-008: the application no longer uses a CWD-global results.h5.
|
||||
TEST(LinearStaticCli, DefaultOutputUsesInputSiblingDerivedName) {
|
||||
TempDirectory directory{"paths"};
|
||||
const auto input = directory.Path() / "model.inp";
|
||||
const auto input = directory.Path() / "Beam.Model.INP";
|
||||
WriteText(input, AxialDeck(true, false, false));
|
||||
|
||||
const auto default_output = directory.Path() / "results.h5";
|
||||
const auto default_output = directory.Path() / "Beam.Model_results.h5";
|
||||
{
|
||||
CurrentDirectoryGuard current_directory{directory.Path()};
|
||||
const auto result = RunApplication({input.string()});
|
||||
ASSERT_EQ(result.exit_code, 0) << result.standard_error;
|
||||
}
|
||||
ExpectFesaHdf5Identity(default_output, input);
|
||||
|
||||
const auto explicit_output = directory.Path() / "named-output.h5";
|
||||
const auto result =
|
||||
RunApplication(ExplicitOutputArguments(input, explicit_output));
|
||||
ASSERT_EQ(result.exit_code, 0) << result.standard_error;
|
||||
ExpectFesaHdf5Identity(explicit_output, input);
|
||||
EXPECT_FALSE(std::filesystem::exists(directory.Path() / "results.h5"));
|
||||
}
|
||||
|
||||
TEST(LinearStaticCli, ReturnsEveryExactExitCodeAndOrderedDiagnostic) {
|
||||
@@ -369,8 +377,7 @@ TEST(LinearStaticCli, ReturnsEveryExactExitCodeAndOrderedDiagnostic) {
|
||||
WriteText(model_input, AxialDeck(true, true, false));
|
||||
WriteText(solver_input, AxialDeck(false, false, false));
|
||||
|
||||
const auto success = RunApplication(
|
||||
ExplicitOutputArguments(valid_input, directory.Path() / "success.h5"));
|
||||
const auto success = RunApplication({valid_input.string()});
|
||||
const auto usage = RunApplication({});
|
||||
const auto missing_input = directory.Path() / "missing.inp";
|
||||
const auto input = RunApplication({missing_input.string()});
|
||||
@@ -378,25 +385,19 @@ TEST(LinearStaticCli, ReturnsEveryExactExitCodeAndOrderedDiagnostic) {
|
||||
RunApplication({missing_input.string(), "--output", "--output"});
|
||||
const auto unknown_output_option =
|
||||
RunApplication({missing_input.string(), "--output", "--bogus"});
|
||||
const auto model = RunApplication(ExplicitOutputArguments(
|
||||
model_input, directory.Path() / "model-failure.h5"));
|
||||
const auto solver = RunApplication(ExplicitOutputArguments(
|
||||
solver_input, directory.Path() / "solver-failure.h5"));
|
||||
const auto output = RunApplication(ExplicitOutputArguments(
|
||||
valid_input, directory.Path() / "nonexistent-parent" / "results.h5"));
|
||||
const auto model = RunApplication({model_input.string()});
|
||||
const auto solver = RunApplication({solver_input.string()});
|
||||
|
||||
EXPECT_EQ(success.exit_code, 0) << success.standard_error;
|
||||
EXPECT_EQ(usage.exit_code, 2);
|
||||
EXPECT_EQ(input.exit_code, 3);
|
||||
EXPECT_EQ(model.exit_code, 4);
|
||||
EXPECT_EQ(solver.exit_code, 5);
|
||||
EXPECT_EQ(output.exit_code, 6);
|
||||
|
||||
ExpectDiagnosticFieldOrder(usage.standard_error);
|
||||
ExpectDiagnosticFieldOrder(input.standard_error);
|
||||
ExpectDiagnosticFieldOrder(model.standard_error);
|
||||
ExpectDiagnosticFieldOrder(solver.standard_error);
|
||||
ExpectDiagnosticFieldOrder(output.standard_error);
|
||||
EXPECT_EQ(repeated_output.exit_code, 2);
|
||||
ExpectDiagnosticFieldOrder(repeated_output.standard_error);
|
||||
EXPECT_NE(repeated_output.standard_error.find("code=cli-usage"),
|
||||
@@ -407,24 +408,79 @@ TEST(LinearStaticCli, ReturnsEveryExactExitCodeAndOrderedDiagnostic) {
|
||||
std::string::npos);
|
||||
}
|
||||
|
||||
// NSNR-CLI-003 and NSNR-CLI-007: usage failure precedes every output side
|
||||
// effect, including replacement of existing derived files.
|
||||
TEST(LinearStaticCli, OutputSubstringRejectionLeavesDerivedFilesUnchanged) {
|
||||
TempDirectory directory{"usage-no-output"};
|
||||
const auto input = directory.Path() / "model.inp";
|
||||
const auto results = directory.Path() / "model_results.h5";
|
||||
const auto log = directory.Path() / "model_log.txt";
|
||||
WriteText(input, AxialDeck(true, false, false));
|
||||
WriteText(results, "previous-results");
|
||||
WriteText(log, "previous-log");
|
||||
const FileSnapshot previous_results = SnapshotFile(results);
|
||||
const FileSnapshot previous_log = SnapshotFile(log);
|
||||
|
||||
for (const char* argument :
|
||||
{"abc--outputdef", "--output-path", "--output=foo", "--output"}) {
|
||||
const auto result = RunApplication({input.string(), argument});
|
||||
EXPECT_EQ(result.exit_code, 2) << argument;
|
||||
EXPECT_EQ(
|
||||
std::count(result.standard_error.begin(), result.standard_error.end(), '\n'),
|
||||
1U)
|
||||
<< result.standard_error;
|
||||
EXPECT_NE(result.standard_error.find("code=cli-usage"),
|
||||
std::string::npos);
|
||||
EXPECT_EQ(SnapshotFile(results).contents, previous_results.contents);
|
||||
EXPECT_EQ(SnapshotFile(results).write_time, previous_results.write_time);
|
||||
EXPECT_EQ(SnapshotFile(log).contents, previous_log.contents);
|
||||
EXPECT_EQ(SnapshotFile(log).write_time, previous_log.write_time);
|
||||
}
|
||||
}
|
||||
|
||||
// NSNR-CLI-006: a held lease blocks the application before any public output
|
||||
// is opened, truncated, or replaced.
|
||||
TEST(LinearStaticCli, BusyOutputLeaseLeavesDerivedFilesUnchanged) {
|
||||
TempDirectory directory{"busy-output"};
|
||||
const auto input = directory.Path() / "model.inp";
|
||||
const auto results = directory.Path() / "model_results.h5";
|
||||
const auto log = directory.Path() / "model_log.txt";
|
||||
WriteText(input, AxialDeck(true, false, false));
|
||||
WriteText(results, "previous-results");
|
||||
WriteText(log, "previous-log");
|
||||
const FileSnapshot previous_results = SnapshotFile(results);
|
||||
const FileSnapshot previous_log = SnapshotFile(log);
|
||||
|
||||
const fesa::DerivedOutputPaths paths{input, results, log};
|
||||
auto lease = fesa::AcquireOutputWriteLease(paths);
|
||||
ASSERT_TRUE(lease.HasValue()) << lease.GetStatus().Diagnostics().size();
|
||||
|
||||
const auto result = RunApplication({input.string()});
|
||||
EXPECT_EQ(result.exit_code, 6) << result.standard_error;
|
||||
EXPECT_NE(result.standard_error.find("code=output-busy"),
|
||||
std::string::npos);
|
||||
EXPECT_EQ(SnapshotFile(results).contents, previous_results.contents);
|
||||
EXPECT_EQ(SnapshotFile(results).write_time, previous_results.write_time);
|
||||
EXPECT_EQ(SnapshotFile(log).contents, previous_log.contents);
|
||||
EXPECT_EQ(SnapshotFile(log).write_time, previous_log.write_time);
|
||||
}
|
||||
|
||||
TEST(LinearStaticCli, OutputRequestsDoNotFilterMandatoryResults) {
|
||||
TempDirectory directory{"output-requests"};
|
||||
const auto plain_input = directory.Path() / "plain.inp";
|
||||
const auto requested_input = directory.Path() / "requested.inp";
|
||||
const auto plain_output = directory.Path() / "plain.h5";
|
||||
const auto requested_output = directory.Path() / "requested.h5";
|
||||
WriteText(plain_input, AxialDeck(true, false, false));
|
||||
WriteText(requested_input, AxialDeck(true, false, true));
|
||||
|
||||
const auto plain =
|
||||
RunApplication(ExplicitOutputArguments(plain_input, plain_output));
|
||||
const auto requested = RunApplication(
|
||||
ExplicitOutputArguments(requested_input, requested_output));
|
||||
const auto plain = RunApplication({plain_input.string()});
|
||||
const auto requested = RunApplication({requested_input.string()});
|
||||
ASSERT_EQ(plain.exit_code, 0) << plain.standard_error;
|
||||
ASSERT_EQ(requested.exit_code, 0) << requested.standard_error;
|
||||
|
||||
const auto plain_file = OpenFile(plain_output);
|
||||
const auto requested_file = OpenFile(requested_output);
|
||||
const auto plain_file =
|
||||
OpenFile(plain_input.parent_path() / "plain_results.h5");
|
||||
const auto requested_file =
|
||||
OpenFile(requested_input.parent_path() / "requested_results.h5");
|
||||
ExpectMandatoryInventory(plain_file.Get());
|
||||
ExpectMandatoryInventory(requested_file.Get());
|
||||
|
||||
@@ -449,10 +505,10 @@ TEST(LinearStaticCli, OutputRequestsDoNotFilterMandatoryResults) {
|
||||
TEST(Mitc4ShellCli, WritesShellHdf5ThroughTheExistingApplicationRoute) {
|
||||
TempDirectory directory{"shell-route"};
|
||||
const auto input = directory.Path() / "shell.inp";
|
||||
const auto output = directory.Path() / "shell-results.h5";
|
||||
const auto output = directory.Path() / "shell_results.h5";
|
||||
WriteText(input, AllConstrainedShellDeck());
|
||||
|
||||
const auto result = RunApplication(ExplicitOutputArguments(input, output));
|
||||
const auto result = RunApplication({input.string()});
|
||||
ASSERT_EQ(result.exit_code, 0) << result.standard_error;
|
||||
ExpectShellHdf5Identity(output, input);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user