fix(input): enforce strict Abaqus subset contract
This commit is contained in:
@@ -74,7 +74,7 @@ TEST(AbaqusParser, ParsesCaseInsensitiveKeywordsCommentsAndCommaFields) {
|
||||
EXPECT_EQ(nodes.data[0], (std::vector<std::string>{
|
||||
"1", "0.0", "0.0", "0.0"}));
|
||||
EXPECT_EQ(nodes.data[1], (std::vector<std::string>{
|
||||
"2", "1.0", "0.0", "0.0", ""}));
|
||||
"2", "1.0", "0.0", "0.0"}));
|
||||
EXPECT_EQ(nodes.source.file, path);
|
||||
EXPECT_EQ(nodes.source.line, 3U);
|
||||
EXPECT_EQ(nodes.source.column, 1U);
|
||||
@@ -121,6 +121,214 @@ TEST(AbaqusParser, RejectsUnsupportedKeywordsInsteadOfIgnoringThem) {
|
||||
EXPECT_EQ(result.diagnostics[0].source->line, 2U);
|
||||
}
|
||||
|
||||
TEST(AbaqusParser, RejectsUnsupportedParametersOnSupportedKeywords) {
|
||||
struct Case final {
|
||||
std::string_view name;
|
||||
std::string_view contents;
|
||||
std::size_t line;
|
||||
};
|
||||
const Case cases[]{
|
||||
{"node", "*NODE, EXTRA=1\n1, 0.0, 0.0, 0.0\n", 1U},
|
||||
{"element", "*ELEMENT, TYPE=B31, EXTRA=1\n1, 1, 2\n", 1U},
|
||||
{"part", "*PART, NAME=P, EXTRA=1\n*END PART\n", 1U},
|
||||
{"end-part", "*PART, NAME=P\n*END PART, EXTRA=1\n", 2U},
|
||||
{
|
||||
"assembly",
|
||||
"*ASSEMBLY, NAME=A, EXTRA=1\n*END ASSEMBLY\n",
|
||||
1U,
|
||||
},
|
||||
{
|
||||
"end-assembly",
|
||||
"*ASSEMBLY, NAME=A\n*END ASSEMBLY, EXTRA=1\n",
|
||||
2U,
|
||||
},
|
||||
{
|
||||
"instance",
|
||||
"*ASSEMBLY, NAME=A\n"
|
||||
"*INSTANCE, NAME=I, PART=P, EXTRA=1\n"
|
||||
"*END INSTANCE\n"
|
||||
"*END ASSEMBLY\n",
|
||||
2U,
|
||||
},
|
||||
{
|
||||
"end-instance",
|
||||
"*ASSEMBLY, NAME=A\n"
|
||||
"*INSTANCE, NAME=I, PART=P\n"
|
||||
"*END INSTANCE, EXTRA=1\n"
|
||||
"*END ASSEMBLY\n",
|
||||
3U,
|
||||
},
|
||||
{"nset", "*NSET, NSET=S, EXTRA=1\n1\n", 1U},
|
||||
{"elset", "*ELSET, ELSET=S, EXTRA=1\n1\n", 1U},
|
||||
{"material", "*MATERIAL, NAME=M, EXTRA=1\n", 1U},
|
||||
{
|
||||
"elastic",
|
||||
"*MATERIAL, NAME=M\n*ELASTIC, EXTRA=1\n1.0, 0.3\n",
|
||||
2U,
|
||||
},
|
||||
{
|
||||
"beam-general-section",
|
||||
"*BEAM GENERAL SECTION, SECTION=GENERAL, ELSET=S, "
|
||||
"MATERIAL=M, EXTRA=1\n"
|
||||
"1.0, 1.0, 0.0, 1.0, 1.0\n"
|
||||
"0.0, 1.0, 0.0\n",
|
||||
1U,
|
||||
},
|
||||
{
|
||||
"transverse-shear-stiffness",
|
||||
"*TRANSVERSE SHEAR STIFFNESS, EXTRA=1\n1.0, 1.0, 0.0\n",
|
||||
1U,
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto& test_case : cases) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-parser-unsupported-parameter-" +
|
||||
std::string{test_case.name} + ".inp",
|
||||
test_case.contents,
|
||||
};
|
||||
|
||||
const auto result = fesa::parse_deck(input.path());
|
||||
|
||||
SCOPED_TRACE(test_case.name);
|
||||
EXPECT_FALSE(result.deck.has_value());
|
||||
ASSERT_EQ(result.diagnostics.size(), 1U);
|
||||
EXPECT_EQ(
|
||||
result.diagnostics[0].code,
|
||||
"abaqus.syntax.unsupported_parameter");
|
||||
ASSERT_TRUE(result.diagnostics[0].source.has_value());
|
||||
EXPECT_EQ(result.diagnostics[0].source->line, test_case.line);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AbaqusParser, RejectsInvalidParameterFormsAndSetScopeOptions) {
|
||||
struct Case final {
|
||||
std::string_view name;
|
||||
std::string_view contents;
|
||||
std::string_view code;
|
||||
};
|
||||
const Case cases[]{
|
||||
{
|
||||
"generate-valued",
|
||||
"*NSET, NSET=S, GENERATE=YES\n1, 2, 1\n",
|
||||
"abaqus.syntax.unsupported_parameter",
|
||||
},
|
||||
{
|
||||
"generate-empty-valued",
|
||||
"*NSET, NSET=S, GENERATE=\n1, 2, 1\n",
|
||||
"abaqus.syntax.unsupported_parameter",
|
||||
},
|
||||
{
|
||||
"empty-element-set",
|
||||
"*ELEMENT, TYPE=B31, ELSET=\n1, 1, 2\n",
|
||||
"abaqus.syntax.invalid_parameter",
|
||||
},
|
||||
{
|
||||
"instance-on-flat-set",
|
||||
"*NSET, NSET=S, INSTANCE=I\n1\n",
|
||||
"abaqus.syntax.unsupported_parameter",
|
||||
},
|
||||
{
|
||||
"instance-on-part-set",
|
||||
"*PART, NAME=P\n"
|
||||
"*NSET, NSET=S, INSTANCE=I\n"
|
||||
"1\n"
|
||||
"*END PART\n",
|
||||
"abaqus.syntax.unsupported_parameter",
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto& test_case : cases) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-parser-parameter-form-" + std::string{test_case.name} +
|
||||
".inp",
|
||||
test_case.contents,
|
||||
};
|
||||
|
||||
const auto result = fesa::parse_deck(input.path());
|
||||
|
||||
SCOPED_TRACE(test_case.name);
|
||||
EXPECT_FALSE(result.deck.has_value());
|
||||
ASSERT_EQ(result.diagnostics.size(), 1U);
|
||||
EXPECT_EQ(result.diagnostics[0].code, test_case.code);
|
||||
ASSERT_TRUE(result.diagnostics[0].source.has_value());
|
||||
EXPECT_EQ(result.diagnostics[0].source->line, 1U +
|
||||
(test_case.name == "instance-on-part-set" ? 1U : 0U));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AbaqusParser, RejectsModelKeywordsOutsideTheirDocumentedScopes) {
|
||||
struct Case final {
|
||||
std::string_view name;
|
||||
std::string_view contents;
|
||||
std::string_view code;
|
||||
std::size_t line;
|
||||
};
|
||||
const Case cases[]{
|
||||
{
|
||||
"node-in-assembly",
|
||||
"*ASSEMBLY, NAME=A\n"
|
||||
"*NODE\n"
|
||||
"1, 0.0, 0.0, 0.0\n"
|
||||
"*END ASSEMBLY\n",
|
||||
"abaqus.syntax.invalid_node_scope",
|
||||
2U,
|
||||
},
|
||||
{
|
||||
"material-in-part",
|
||||
"*PART, NAME=P\n"
|
||||
"*MATERIAL, NAME=M\n"
|
||||
"*END PART\n",
|
||||
"abaqus.syntax.invalid_material_scope",
|
||||
2U,
|
||||
},
|
||||
{
|
||||
"section-in-assembly",
|
||||
"*ASSEMBLY, NAME=A\n"
|
||||
"*BEAM GENERAL SECTION, SECTION=GENERAL, ELSET=S, MATERIAL=M\n"
|
||||
"1.0, 1.0, 0.0, 1.0, 1.0\n"
|
||||
"0.0, 1.0, 0.0\n"
|
||||
"*END ASSEMBLY\n",
|
||||
"abaqus.syntax.invalid_section_scope",
|
||||
2U,
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto& test_case : cases) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-parser-invalid-scope-" + std::string{test_case.name} +
|
||||
".inp",
|
||||
test_case.contents,
|
||||
};
|
||||
|
||||
const auto result = fesa::parse_deck(input.path());
|
||||
|
||||
SCOPED_TRACE(test_case.name);
|
||||
EXPECT_FALSE(result.deck.has_value());
|
||||
ASSERT_EQ(result.diagnostics.size(), 1U);
|
||||
EXPECT_EQ(result.diagnostics[0].code, test_case.code);
|
||||
ASSERT_TRUE(result.diagnostics[0].source.has_value());
|
||||
EXPECT_EQ(result.diagnostics[0].source->line, test_case.line);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AbaqusParser, RejectsDataRowsOnMaterial) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-parser-material-data.inp",
|
||||
"*MATERIAL, NAME=Steel\n"
|
||||
"unexpected data\n"};
|
||||
|
||||
const auto result = fesa::parse_deck(input.path());
|
||||
|
||||
EXPECT_FALSE(result.deck.has_value());
|
||||
ASSERT_EQ(result.diagnostics.size(), 1U);
|
||||
EXPECT_EQ(
|
||||
result.diagnostics[0].code,
|
||||
"abaqus.syntax.data_without_keyword");
|
||||
ASSERT_TRUE(result.diagnostics[0].source.has_value());
|
||||
EXPECT_EQ(result.diagnostics[0].source->line, 2U);
|
||||
}
|
||||
|
||||
TEST(ScopedDeck, PreservesPartAssemblyAndInstanceScopes) {
|
||||
const auto path =
|
||||
fixture_path("minimal_part_instance_cantilever.inp");
|
||||
|
||||
Reference in New Issue
Block a user