2026-07-07 10:53:16 +02:00
|
|
|
// This file was generated with the assistance of an AI coding tool.
|
|
|
|
|
|
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
2026-08-09 12:42:35 +02:00
|
|
|
#include <ifcparse/file.h>
|
2026-08-22 13:12:36 +02:00
|
|
|
#include <ifcparse/parse.h>
|
2026-08-09 12:42:35 +02:00
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
2026-07-07 10:53:16 +02:00
|
|
|
|
2026-08-22 13:12:36 +02:00
|
|
|
TEST_CASE("SPF strings can be encoded and decoded", "[ifcparse]") {
|
|
|
|
|
const std::string decoded = "Caf\xC3\xA9" "'s \\";
|
|
|
|
|
const std::string encoded = R"('Caf\X2\00E9\X0\''s \\')";
|
|
|
|
|
|
|
|
|
|
CHECK(ifcopenshell::encode_spf_string(decoded) == encoded);
|
|
|
|
|
CHECK(ifcopenshell::decode_spf_string(encoded) == decoded);
|
|
|
|
|
CHECK(ifcopenshell::decode_spf_string(encoded.substr(1, encoded.size() - 2)) == decoded);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-09 12:42:35 +02:00
|
|
|
TEST_CASE("IfcPropertySetDefinitionSet references are resolved without replacing their owner", "[ifcparse]") {
|
|
|
|
|
const std::string fixture = std::string(IFCOPENSHELL_TEST_FIXTURES) + "/ColumnPSetsOfSets.ifc";
|
|
|
|
|
ifcopenshell::file file(fixture);
|
|
|
|
|
|
|
|
|
|
REQUIRE(file.good());
|
|
|
|
|
|
|
|
|
|
const auto relationship = file.instance_by_id(139);
|
|
|
|
|
REQUIRE(relationship);
|
|
|
|
|
CHECK(relationship.id() == 139);
|
|
|
|
|
CHECK(relationship.declaration().name() == "IfcRelDefinesByProperties");
|
|
|
|
|
|
|
|
|
|
const express::base definition_set = relationship.get_attribute_value(5);
|
|
|
|
|
REQUIRE(definition_set);
|
|
|
|
|
CHECK(definition_set.declaration().name() == "IfcPropertySetDefinitionSet");
|
|
|
|
|
|
|
|
|
|
const std::vector<express::base> definitions = definition_set.get_attribute_value(0);
|
|
|
|
|
REQUIRE(definitions.size() == 2);
|
|
|
|
|
CHECK(definitions[0].id() == 136);
|
|
|
|
|
CHECK(definitions[1].id() == 138);
|
2026-07-07 10:53:16 +02:00
|
|
|
}
|
2026-08-09 12:56:15 +02:00
|
|
|
|
|
|
|
|
TEST_CASE("Bypassed entity types include their subtypes", "[ifcparse]") {
|
|
|
|
|
const std::string fixture = std::string(IFCOPENSHELL_TEST_FIXTURES) + "/ColumnPSetsOfSets.ifc";
|
|
|
|
|
ifcopenshell::file file(ifcopenshell::uninitialized_tag{});
|
|
|
|
|
file.bypass_type("IfcRepresentationItem");
|
|
|
|
|
|
|
|
|
|
REQUIRE(file.initialize(fixture));
|
|
|
|
|
CHECK(file.instances_by_type("IfcRepresentationItem").empty());
|
|
|
|
|
CHECK(file.instances_by_type("IfcCartesianPoint").empty());
|
|
|
|
|
}
|
2026-08-28 10:37:19 +02:00
|
|
|
|
|
|
|
|
TEST_CASE("Aggregate inverse updates preserve reference multiplicity", "[ifcparse]") {
|
|
|
|
|
ifcopenshell::file file(ifcopenshell::schema_by_name("IFC4"));
|
|
|
|
|
const auto* segment_declaration = file.schema()->declaration_by_name("IfcCompositeCurveSegment");
|
|
|
|
|
auto curve = file.create(file.schema()->declaration_by_name("IfcCompositeCurve"));
|
|
|
|
|
auto segment_a = file.create(segment_declaration);
|
|
|
|
|
auto segment_b = file.create(segment_declaration);
|
|
|
|
|
auto segment_c = file.create(segment_declaration);
|
|
|
|
|
auto segment_d = file.create(segment_declaration);
|
|
|
|
|
const auto inverse_count = [&file](const express::base& instance) {
|
|
|
|
|
return file.instances_by_reference(instance.id()).size();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
curve.set_attribute_value(0, std::vector<express::base>{segment_a, segment_a, segment_b, segment_c});
|
|
|
|
|
CHECK(inverse_count(segment_a) == 2);
|
|
|
|
|
CHECK(inverse_count(segment_b) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_c) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_d) == 0);
|
|
|
|
|
|
|
|
|
|
curve.set_attribute_value(0, std::vector<express::base>{segment_a, segment_a, segment_b, segment_c, segment_d});
|
|
|
|
|
CHECK(inverse_count(segment_a) == 2);
|
|
|
|
|
CHECK(inverse_count(segment_b) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_c) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_d) == 1);
|
|
|
|
|
|
|
|
|
|
curve.set_attribute_value(0, std::vector<express::base>{segment_a, segment_a, segment_b, segment_c});
|
|
|
|
|
CHECK(inverse_count(segment_a) == 2);
|
|
|
|
|
CHECK(inverse_count(segment_b) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_c) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_d) == 0);
|
|
|
|
|
|
|
|
|
|
const std::vector<express::base> reordered{segment_c, segment_a, segment_b, segment_a};
|
|
|
|
|
curve.set_attribute_value(0, reordered);
|
|
|
|
|
CHECK((std::vector<express::base>)curve.get_attribute_value(0) == reordered);
|
|
|
|
|
CHECK(inverse_count(segment_a) == 2);
|
|
|
|
|
CHECK(inverse_count(segment_b) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_c) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_d) == 0);
|
|
|
|
|
|
|
|
|
|
curve.set_attribute_value(0, std::vector<express::base>{segment_a, segment_b, segment_b, segment_d});
|
|
|
|
|
CHECK(inverse_count(segment_a) == 1);
|
|
|
|
|
CHECK(inverse_count(segment_b) == 2);
|
|
|
|
|
CHECK(inverse_count(segment_c) == 0);
|
|
|
|
|
CHECK(inverse_count(segment_d) == 1);
|
|
|
|
|
|
|
|
|
|
curve.set_attribute_value(0, std::vector<express::base>{segment_c, segment_c, segment_d});
|
|
|
|
|
CHECK(inverse_count(segment_a) == 0);
|
|
|
|
|
CHECK(inverse_count(segment_b) == 0);
|
|
|
|
|
CHECK(inverse_count(segment_c) == 2);
|
|
|
|
|
CHECK(inverse_count(segment_d) == 1);
|
|
|
|
|
}
|