diff --git a/phases/abaqus-subset-completion/index.json b/phases/abaqus-subset-completion/index.json index 21b4ac5..df7bbd8 100644 --- a/phases/abaqus-subset-completion/index.json +++ b/phases/abaqus-subset-completion/index.json @@ -6,7 +6,7 @@ "step": 0, "name": "abaqus-input-contract", "status": "completed", - "summary": "Defined the normative Phase 1 Abaqus subset and expanded the public parser/mapper fixture matrix to 69 cases covering strict scopes, parameter forms, data ownership, exact mesh rows, duplicate Parts, Assembly-only hierarchical targets, and source-accurate diagnostics.", + "summary": "Defined the normative Phase 1 Abaqus subset and expanded the public parser/mapper fixture matrix to 74 cases covering strict scopes, parameter forms, data ownership, exact mesh rows and references in active and inactive Parts, duplicate Parts, Assembly-only hierarchical targets, and source-accurate diagnostics.", "started_at": "2026-08-01T02:16:39+0900", "completed_at": "2026-08-01T02:24:23+0900" }, diff --git a/src/fesa/io/abaqus/semantic_mapper.cpp b/src/fesa/io/abaqus/semantic_mapper.cpp index d9ff9a4..e1b1af7 100644 --- a/src/fesa/io/abaqus/semantic_mapper.cpp +++ b/src/fesa/io/abaqus/semantic_mapper.cpp @@ -77,18 +77,21 @@ public: instance_name_ = std::move(selected.input->instance_name); collect_materials(); - collect_nodes(); + collect_nodes(active_records_, node_ids_, true); SetResolutionResult resolved_sets = resolve_sets(deck_); if (!resolved_sets.diagnostics.empty()) { diagnostics_.insert( diagnostics_.end(), std::make_move_iterator(resolved_sets.diagnostics.begin()), std::make_move_iterator(resolved_sets.diagnostics.end())); - } else { + } + validate_inactive_parts(resolved_sets.sets); + if (resolved_sets.diagnostics.empty()) { collect_resolved_sets(resolved_sets.sets); } - collect_sections(); - collect_elements(); + collect_sections(active_records_, active_element_sets_, true); + collect_elements( + active_records_, node_ids_, element_ids_, true); emit_sets(); collect_step(); @@ -318,8 +321,11 @@ private: } } - void collect_nodes() { - for (const DeckRecord& record : active_records_) { + void collect_nodes( + const std::span records, + LabelMap& node_ids, + const bool emit) { + for (const DeckRecord& record : records) { if (record.keyword != "NODE") { continue; } @@ -356,7 +362,7 @@ private: continue; } - if (node_ids_.contains(*label)) { + if (node_ids.contains(*label)) { add_error( "abaqus.semantic.duplicate_node_label", "Node label " + std::to_string(*label) + @@ -365,17 +371,151 @@ private: continue; } - const NodeId id{next_node_id_++}; - node_ids_.emplace(*label, id); - builder_.add_node({ - id, - EntityOrigin{part_name_, instance_name_, *label}, - Vec3{*x, *y, *z}, - }); + const NodeId id = emit + ? NodeId{next_node_id_++} + : NodeId{static_cast( + node_ids.size())}; + node_ids.emplace(*label, id); + if (emit) { + builder_.add_node({ + id, + EntityOrigin{part_name_, instance_name_, *label}, + Vec3{*x, *y, *z}, + }); + } } } } + void validate_inactive_mesh(const ParsedPart& part) { + LabelMap node_ids; + ElementLabelMap element_ids; + collect_nodes(part.records, node_ids, false); + collect_elements(part.records, node_ids, element_ids, false); + } + + struct SectionData final { + std::array properties{}; + std::array orientation{}; + std::optional> shear_values; + bool valid = true; + }; + + SectionData validate_section_data( + const DeckRecord& record, + const DeckRecord* shear) { + SectionData result; + if (record.data.size() != 2U || + record.data[0].size() != result.properties.size() || + record.data[1].size() != result.orientation.size()) { + SourceLocation source = record.source; + if (!record.data.empty()) { + source = record.data[0].size() != result.properties.size() + ? data_source(record, 0U) + : data_source( + record, + record.data.size() > 2U ? 2U : 1U); + } + add_error( + "abaqus.semantic.invalid_section_data", + "*BEAM GENERAL SECTION requires exactly A, Iy, Iyz, Iz, J " + "and one three-component orientation row.", + source); + result.valid = false; + } else { + bool valid_properties = true; + for (std::size_t field = 0; field < result.properties.size(); + ++field) { + valid_properties = + parse_number( + record.data[0][field], result.properties[field]) && + valid_properties; + } + valid_properties = + valid_properties && result.properties[0] > 0.0 && + result.properties[1] > 0.0 && result.properties[2] == 0.0 && + result.properties[3] > 0.0 && result.properties[4] > 0.0; + if (!valid_properties) { + add_error( + "abaqus.semantic.invalid_section_data", + "General Beam properties require finite positive A, Iy, " + "Iz, J and Iyz=0.", + data_source(record, 0U)); + } + + bool valid_orientation = true; + for (std::size_t field = 0; field < result.orientation.size(); + ++field) { + valid_orientation = + parse_number( + record.data[1][field], result.orientation[field]) && + valid_orientation; + } + if (!valid_orientation) { + add_error( + "abaqus.semantic.invalid_section_data", + "Beam section orientation components must be finite.", + data_source(record, 1U)); + } + result.valid = valid_properties && valid_orientation; + } + + if (shear == nullptr) { + return result; + } + const SourceLocation source = + shear->data.empty() ? shear->source : data_source(*shear, 0U); + std::array values{}; + bool valid_shear = shear->data.size() == 1U && + shear->data[0].size() == values.size(); + if (valid_shear) { + for (std::size_t field = 0; field < values.size(); ++field) { + valid_shear = + parse_number(shear->data[0][field], values[field]) && + valid_shear; + } + } + if (!valid_shear || values[0] <= 0.0 || values[1] <= 0.0) { + add_error( + "abaqus.semantic.invalid_transverse_shear_data", + "*TRANSVERSE SHEAR STIFFNESS requires finite positive K23, " + "K13 and numeric SCF=0.", + source); + result.valid = false; + } else if (values[2] != 0.0) { + add_error( + "abaqus.semantic.nonzero_scf", + "Phase 1 supports only SCF=0.", + source); + result.valid = false; + } else { + result.shear_values = values; + } + return result; + } + + void validate_inactive_parts( + const std::vector& resolved_sets) { + if (flat_) { + return; + } + for (const ParsedPart& part : deck_.parts) { + if (part.name == part_name_) { + continue; + } + validate_inactive_mesh(part); + RawSetMap element_sets; + for (const ResolvedSet& set : resolved_sets) { + if (set.scope == ResolvedSetScope::part && + set.scope_name == part.name && + set.kind == ResolvedSetKind::element) { + element_sets[set.set_name] = set.sorted_unique_labels; + } + } + collect_sections(part.records, element_sets, false); + } + } + void collect_resolved_sets(const std::vector& sets) { for (const ResolvedSet& set : sets) { const bool mesh_scope = @@ -417,9 +557,14 @@ private: } } - void collect_sections() { - for (std::size_t index = 0; index < active_records_.size(); ++index) { - const DeckRecord& record = active_records_[index]; + void collect_sections( + const std::span records, + const RawSetMap& element_sets, + const bool emit) { + std::set> assigned_set_names; + std::set assigned_elements; + for (std::size_t index = 0; index < records.size(); ++index) { + const DeckRecord& record = records[index]; if (record.keyword == "TRANSVERSE SHEAR STIFFNESS") { add_error( "abaqus.semantic.orphan_transverse_shear", @@ -433,10 +578,10 @@ private: } const DeckRecord* shear = nullptr; - if (index + 1U < active_records_.size() && - active_records_[index + 1U].keyword == + if (index + 1U < records.size() && + records[index + 1U].keyword == "TRANSVERSE SHEAR STIFFNESS") { - shear = &active_records_[index + 1U]; + shear = &records[index + 1U]; ++index; } @@ -459,7 +604,7 @@ private: } if (element_set != nullptr && - !assigned_element_set_names_.insert(*element_set).second) { + !assigned_set_names.insert(*element_set).second) { add_error( "abaqus.semantic.duplicate_section_assignment", "Element set '" + *element_set + @@ -481,10 +626,10 @@ private: } } - auto resolved_set = active_element_sets_.end(); + auto resolved_set = element_sets.end(); if (element_set != nullptr) { - resolved_set = active_element_sets_.find(*element_set); - if (resolved_set == active_element_sets_.end()) { + resolved_set = element_sets.find(*element_set); + if (resolved_set == element_sets.end()) { add_error( "abaqus.semantic.missing_element_set", "Beam section references missing element set '" + @@ -494,132 +639,48 @@ private: } } - std::array properties{}; - std::array orientation{}; - if (record.data.size() != 2U || - record.data[0].size() != properties.size() || - record.data[1].size() != orientation.size()) { - SourceLocation section_data_source = record.source; - if (!record.data.empty()) { - if (record.data[0].size() != properties.size()) { - section_data_source = data_source(record, 0U); - } else if (record.data.size() > 2U) { - section_data_source = data_source(record, 2U); - } else if (record.data.size() == 2U) { - section_data_source = data_source(record, 1U); - } - } - add_error( - "abaqus.semantic.invalid_section_data", - "*BEAM GENERAL SECTION requires exactly A, Iy, Iyz, Iz, " - "J and one three-component orientation row.", - section_data_source); - valid = false; - } else { - bool valid_properties = true; - for (std::size_t field = 0; field < properties.size(); ++field) { - valid_properties = parse_number( - record.data[0][field], - properties[field]) && - valid_properties; - } - valid_properties = - valid_properties && properties[0] > 0.0 && - properties[1] > 0.0 && properties[2] == 0.0 && - properties[3] > 0.0 && properties[4] > 0.0; - if (!valid_properties) { - add_error( - "abaqus.semantic.invalid_section_data", - "General Beam properties require finite positive A, " - "Iy, Iz, J and Iyz=0.", - data_source(record, 0U)); - } - - bool valid_orientation = true; - for (std::size_t field = 0; field < orientation.size(); ++field) { - valid_orientation = parse_number( - record.data[1][field], - orientation[field]) && - valid_orientation; - } - if (!valid_orientation) { - add_error( - "abaqus.semantic.invalid_section_data", - "Beam section orientation components must be finite.", - data_source(record, 1U)); - } - valid = valid && valid_properties && valid_orientation; - } + SectionData section_data = validate_section_data(record, shear); + valid = valid && section_data.valid; double shear_area_y = 0.0; double shear_area_z = 0.0; ShearPropertySource shear_source = ShearPropertySource::phase1_default; if (shear == nullptr) { - shear_area_y = 5.0 * properties[0] / 6.0; + shear_area_y = 5.0 * section_data.properties[0] / 6.0; shear_area_z = shear_area_y; - } else { + } else if (section_data.shear_values.has_value() && + material != materials_.end()) { const SourceLocation shear_source_location = shear->data.empty() ? shear->source : data_source(*shear, 0U); - std::array values{}; - bool valid_shear = - shear->data.size() == 1U && - shear->data[0].size() == values.size(); - if (valid_shear) { - for (std::size_t field = 0; field < values.size(); ++field) { - valid_shear = parse_number( - shear->data[0][field], values[field]) && - valid_shear; - } - } - if (!valid_shear || values[0] <= 0.0 || values[1] <= 0.0) { + const double shear_modulus = + material->second.young / + (2.0 * (1.0 + material->second.poisson)); + shear_area_y = (*section_data.shear_values)[0] / shear_modulus; + shear_area_z = (*section_data.shear_values)[1] / shear_modulus; + if (!std::isfinite(shear_area_y) || + !std::isfinite(shear_area_z) || shear_area_y <= 0.0 || + shear_area_z <= 0.0) { add_error( "abaqus.semantic.invalid_transverse_shear_data", - "*TRANSVERSE SHEAR STIFFNESS requires finite positive " - "K23, K13 and numeric SCF=0.", + "Transverse stiffness produces an invalid effective " + "shear area.", shear_source_location); valid = false; - } else if (values[2] != 0.0) { - add_error( - "abaqus.semantic.nonzero_scf", - "Phase 1 supports only SCF=0.", - shear_source_location); - valid = false; - } else if (material != materials_.end()) { - const double shear_modulus = - material->second.young / - (2.0 * (1.0 + material->second.poisson)); - shear_area_y = values[0] / shear_modulus; - shear_area_z = values[1] / shear_modulus; - if (!std::isfinite(shear_area_y) || - !std::isfinite(shear_area_z) || - shear_area_y <= 0.0 || shear_area_z <= 0.0) { - add_error( - "abaqus.semantic.invalid_transverse_shear_data", - "Transverse stiffness produces an invalid effective " - "shear area.", - shear_source_location); - valid = false; - } - shear_source = ShearPropertySource::input; } + shear_source = ShearPropertySource::input; } if (!valid || element_set == nullptr || material == materials_.end() || - resolved_set == active_element_sets_.end()) { + resolved_set == element_sets.end()) { continue; } - const SectionId section_id{next_section_id_++}; - const SectionAssignment assignment{ - material->second.id, - section_id, - }; bool duplicate_member = false; for (const std::int64_t label : resolved_set->second) { - if (element_assignments_.contains(label)) { + if (assigned_elements.contains(label)) { duplicate_member = true; break; } @@ -631,6 +692,18 @@ private: record.source); continue; } + for (const std::int64_t label : resolved_set->second) { + assigned_elements.insert(label); + } + if (!emit) { + continue; + } + + const SectionId section_id{next_section_id_++}; + const SectionAssignment assignment{ + material->second.id, + section_id, + }; for (const std::int64_t label : resolved_set->second) { element_assignments_.emplace(label, assignment); } @@ -638,21 +711,28 @@ private: builder_.add_section({ section_id, *element_set, - properties[0], - properties[1], - properties[3], - properties[4], + section_data.properties[0], + section_data.properties[1], + section_data.properties[3], + section_data.properties[4], shear_area_y, shear_area_z, shear_source, - Vec3{orientation[0], orientation[1], orientation[2]}, + Vec3{ + section_data.orientation[0], + section_data.orientation[1], + section_data.orientation[2]}, {}, }); } } - void collect_elements() { - for (const DeckRecord& record : active_records_) { + void collect_elements( + const std::span records, + const LabelMap& node_ids, + ElementLabelMap& element_ids, + const bool emit) { + for (const DeckRecord& record : records) { if (record.keyword != "ELEMENT") { continue; } @@ -702,7 +782,7 @@ private: continue; } - if (element_ids_.contains(*label)) { + if (element_ids.contains(*label)) { add_error( "abaqus.semantic.duplicate_element_label", "Element label " + std::to_string(*label) + @@ -711,19 +791,21 @@ private: continue; } - const auto assignment = element_assignments_.find(*label); - if (assignment == element_assignments_.end()) { - add_error( - "abaqus.semantic.missing_section", - "B31 element " + std::to_string(*label) + - " has no Beam section assignment.", - record.source); - continue; + auto assignment = element_assignments_.end(); + if (emit) { + assignment = element_assignments_.find(*label); + if (assignment == element_assignments_.end()) { + add_error( + "abaqus.semantic.missing_section", + "B31 element " + std::to_string(*label) + + " has no Beam section assignment.", + source); + continue; + } } - - const auto first = node_ids_.find(*first_label); - const auto second = node_ids_.find(*second_label); - if (first == node_ids_.end() || second == node_ids_.end()) { + const auto first = node_ids.find(*first_label); + const auto second = node_ids.find(*second_label); + if (first == node_ids.end() || second == node_ids.end()) { add_error( "abaqus.semantic.missing_node", "B31 element references a missing node label.", @@ -731,8 +813,14 @@ private: continue; } - const ElementId id{next_element_id_++}; - element_ids_.emplace(*label, id); + const ElementId id = emit + ? ElementId{next_element_id_++} + : ElementId{static_cast( + element_ids.size())}; + element_ids.emplace(*label, id); + if (!emit) { + continue; + } builder_.add_beam_element({ id, EntityOrigin{part_name_, instance_name_, *label}, @@ -1041,7 +1129,6 @@ private: std::map, std::less<>> active_element_sets_; std::map element_assignments_; - std::set> assigned_element_set_names_; RawSetMap raw_node_sets_; RawSetMap raw_assembly_node_sets_; RawSetMap raw_element_sets_; diff --git a/tests/fixtures/abaqus/contract.tsv b/tests/fixtures/abaqus/contract.tsv index aef928e..88ee6ab 100644 --- a/tests/fixtures/abaqus/contract.tsv +++ b/tests/fixtures/abaqus/contract.tsv @@ -68,3 +68,8 @@ hierarchical_part_target_invalid invalid invalid/hierarchical_part_set_target.in material_data_invalid invalid invalid/material_data.inp syntax abaqus.syntax.data_without_keyword 2 - - - - - - - - - - - node_coordinate_invalid invalid invalid/node_invalid_coordinate.inp semantic abaqus.semantic.invalid_number 2 - - - - - - - - - - - element_missing_node_invalid invalid invalid/element_missing_node.inp semantic abaqus.semantic.missing_node 4 - - - - - - - - - - - +inactive_part_node_invalid invalid invalid/inactive_part_node_surplus.inp semantic abaqus.semantic.invalid_node_data 3 - - - - - - - - - - - +inactive_part_element_invalid invalid invalid/inactive_part_element_type.inp semantic abaqus.semantic.unsupported_element 2 - - - - - - - - - - - +inactive_part_section_invalid invalid invalid/inactive_part_section_data.inp semantic abaqus.semantic.invalid_section_data 3 - - - - - - - - - - - +inactive_part_duplicate_node_invalid invalid invalid/inactive_part_duplicate_node.inp semantic abaqus.semantic.duplicate_node_label 4 - - - - - - - - - - - +inactive_part_missing_node_invalid invalid invalid/inactive_part_missing_node.inp semantic abaqus.semantic.missing_node 5 - - - - - - - - - - - diff --git a/tests/fixtures/abaqus/invalid/inactive_part_duplicate_node.inp b/tests/fixtures/abaqus/invalid/inactive_part_duplicate_node.inp new file mode 100644 index 0000000..06ac873 --- /dev/null +++ b/tests/fixtures/abaqus/invalid/inactive_part_duplicate_node.inp @@ -0,0 +1,11 @@ +*PART, NAME=Unused +*NODE +1, 0.0, 0.0, 0.0 +1, 1.0, 0.0, 0.0 +*END PART +*PART, NAME=Active +*END PART +*ASSEMBLY, NAME=RootAssembly +*INSTANCE, NAME=Active-1, PART=Active +*END INSTANCE +*END ASSEMBLY diff --git a/tests/fixtures/abaqus/invalid/inactive_part_element_type.inp b/tests/fixtures/abaqus/invalid/inactive_part_element_type.inp new file mode 100644 index 0000000..fb2638f --- /dev/null +++ b/tests/fixtures/abaqus/invalid/inactive_part_element_type.inp @@ -0,0 +1,10 @@ +*PART, NAME=Unused +*ELEMENT, TYPE=B32 +1, 1, 2 +*END PART +*PART, NAME=Active +*END PART +*ASSEMBLY, NAME=RootAssembly +*INSTANCE, NAME=Active-1, PART=Active +*END INSTANCE +*END ASSEMBLY diff --git a/tests/fixtures/abaqus/invalid/inactive_part_missing_node.inp b/tests/fixtures/abaqus/invalid/inactive_part_missing_node.inp new file mode 100644 index 0000000..5920910 --- /dev/null +++ b/tests/fixtures/abaqus/invalid/inactive_part_missing_node.inp @@ -0,0 +1,12 @@ +*PART, NAME=Unused +*NODE +1, 0.0, 0.0, 0.0 +*ELEMENT, TYPE=B31 +1, 1, 2 +*END PART +*PART, NAME=Active +*END PART +*ASSEMBLY, NAME=RootAssembly +*INSTANCE, NAME=Active-1, PART=Active +*END INSTANCE +*END ASSEMBLY diff --git a/tests/fixtures/abaqus/invalid/inactive_part_node_surplus.inp b/tests/fixtures/abaqus/invalid/inactive_part_node_surplus.inp new file mode 100644 index 0000000..34ff27f --- /dev/null +++ b/tests/fixtures/abaqus/invalid/inactive_part_node_surplus.inp @@ -0,0 +1,10 @@ +*PART, NAME=Unused +*NODE +1, 0.0, 0.0, 0.0, 9.0 +*END PART +*PART, NAME=Active +*END PART +*ASSEMBLY, NAME=RootAssembly +*INSTANCE, NAME=Active-1, PART=Active +*END INSTANCE +*END ASSEMBLY diff --git a/tests/fixtures/abaqus/invalid/inactive_part_section_data.inp b/tests/fixtures/abaqus/invalid/inactive_part_section_data.inp new file mode 100644 index 0000000..a30d4df --- /dev/null +++ b/tests/fixtures/abaqus/invalid/inactive_part_section_data.inp @@ -0,0 +1,11 @@ +*PART, NAME=Unused +*BEAM GENERAL SECTION, SECTION=GENERAL, ELSET=Beam, MATERIAL=Steel +0.0, 1.0, 0.0, 1.0, 1.0 +0.0, 1.0, 0.0 +*END PART +*PART, NAME=Active +*END PART +*ASSEMBLY, NAME=RootAssembly +*INSTANCE, NAME=Active-1, PART=Active +*END INSTANCE +*END ASSEMBLY diff --git a/tests/integration/io/minimal_deck_to_domain_test.cpp b/tests/integration/io/minimal_deck_to_domain_test.cpp index c9a11cb..2e8016a 100644 --- a/tests/integration/io/minimal_deck_to_domain_test.cpp +++ b/tests/integration/io/minimal_deck_to_domain_test.cpp @@ -333,6 +333,92 @@ TEST(ActiveInstance, ExcludesPartsNotReferencedByTheInstance) { EXPECT_EQ(result.domain->nodes()[1].origin.part_name, "BeamPart"); } +TEST(ActiveInstance, RejectsInvalidRecordsInUnreferencedParts) { + struct Case final { + std::string_view name; + std::string_view unused_records; + std::string_view code; + std::size_t line; + }; + const Case cases[]{ + { + "node-surplus", + "*NODE\n1, 0.0, 0.0, 0.0, 9.0\n", + "abaqus.semantic.invalid_node_data", + 3U, + }, + { + "element-type", + "*ELEMENT, TYPE=B32\n1, 1, 2\n", + "abaqus.semantic.unsupported_element", + 2U, + }, + { + "duplicate-node", + "*NODE\n1, 0.0, 0.0, 0.0\n1, 1.0, 0.0, 0.0\n", + "abaqus.semantic.duplicate_node_label", + 4U, + }, + { + "missing-node", + "*NODE\n1, 0.0, 0.0, 0.0\n" + "*ELEMENT, TYPE=B31\n1, 1, 2\n", + "abaqus.semantic.missing_node", + 5U, + }, + { + "section-data", + "*BEAM GENERAL SECTION, SECTION=GENERAL, ELSET=Beam, " + "MATERIAL=Steel\n" + "0.0, 1.0, 0.0, 1.0, 1.0\n" + "0.0, 1.0, 0.0\n", + "abaqus.semantic.invalid_section_data", + 3U, + }, + }; + const std::string active_model{ + "*PART, NAME=BeamPart\n" + "*NODE\n" + "1, 0.0, 0.0, 0.0\n" + "2, 1.0, 0.0, 0.0\n" + "*ELEMENT, TYPE=B31, ELSET=Beam\n" + "1, 1, 2\n" + "*ELSET, ELSET=Beam\n" + "1\n" + "*BEAM GENERAL SECTION, SECTION=GENERAL, ELSET=Beam, MATERIAL=Steel\n" + "1.0, 1.0, 0.0, 1.0, 1.0\n" + "0.0, 1.0, 0.0\n" + "*END PART\n" + "*ASSEMBLY, NAME=RootAssembly\n" + "*INSTANCE, NAME=Beam-1, PART=BeamPart\n" + "*END INSTANCE\n" + "*END ASSEMBLY\n" + "*MATERIAL, NAME=Steel\n" + "*ELASTIC\n" + "210000.0, 0.3\n" + "*STEP\n" + "*STATIC\n" + "*END STEP\n"}; + + for (const auto& test_case : cases) { + const TemporaryDeck input{ + "fesa-inactive-part-" + std::string{test_case.name} + ".inp", + "*PART, NAME=Unused\n" + std::string{test_case.unused_records} + + "*END PART\n" + active_model, + }; + + const auto result = parse_and_map(input.path()); + + SCOPED_TRACE(test_case.name); + EXPECT_FALSE(result.domain.has_value()); + const fesa::Diagnostic* diagnostic = + find_diagnostic(result, test_case.code); + ASSERT_NE(diagnostic, nullptr); + ASSERT_TRUE(diagnostic->source.has_value()); + EXPECT_EQ(diagnostic->source->line, test_case.line); + } +} + TEST(ActiveInstance, RejectsInstanceTransformWithSourceDiagnostic) { const TemporaryDeck input{ "fesa-instance-transform.inp", diff --git a/tests/unit/io/abaqus/material_section_mapping_test.cpp b/tests/unit/io/abaqus/material_section_mapping_test.cpp index 821df6a..e9304e1 100644 --- a/tests/unit/io/abaqus/material_section_mapping_test.cpp +++ b/tests/unit/io/abaqus/material_section_mapping_test.cpp @@ -315,7 +315,7 @@ TEST(BeamSection, RejectsElementWithoutSectionAssignment) { const auto result = parse_and_map(input); - expect_diagnostic(result, "abaqus.semantic.missing_section", 4U); + expect_diagnostic(result, "abaqus.semantic.missing_section", 5U); } TEST(BeamSection, RejectsDuplicateSectionAssignment) {