#include #include #include #include #include "vde/foundation/vde_format.h" #include "vde/brep/modeling.h" #include "vde/brep/feature_tree.h" using namespace vde::foundation; using namespace vde::brep; using namespace vde::core; // ═══════════════════════════════════════════════════════════════ // 辅助函数 // ═══════════════════════════════════════════════════════════════ static BrepModel make_test_box() { return make_box(2.0, 3.0, 4.0); } static BrepModel make_test_cylinder() { return make_cylinder(1.0, 5.0); } static void clean(const char* path) { std::remove(path); } static void expect_valid_body(const BrepModel& m) { EXPECT_GT(m.num_vertices(), 0); EXPECT_GT(m.num_edges(), 0); EXPECT_GT(m.num_faces(), 0); EXPECT_GT(m.num_bodies(), 0); } // ═══════════════════════════════════════════════════════════════ // 1. VdeHeader 测试 // ═══════════════════════════════════════════════════════════════ TEST(VdeHeaderTest, DefaultHeader) { VdeHeader hdr; EXPECT_EQ(hdr.magic, VDE_MAGIC_FOURCC); EXPECT_EQ(hdr.version, VDE_FILE_VERSION); EXPECT_EQ(hdr.flags, 0u); EXPECT_EQ(hdr.data_offset, 32u); EXPECT_TRUE(hdr.is_valid()); } TEST(VdeHeaderTest, EncodeDecodeRoundtrip) { VdeHeader hdr; hdr.flags = VDE_FLAG_HAS_ATTRIBUTES | VDE_FLAG_HAS_HISTORY; hdr.data_offset = 64; std::vector buf; hdr.encode(buf); EXPECT_EQ(buf.size(), 32u); VdeHeader parsed = VdeHeader::parse(buf.data(), buf.size()); EXPECT_EQ(parsed.magic, VDE_MAGIC_FOURCC); EXPECT_EQ(parsed.version, VDE_FILE_VERSION); EXPECT_EQ(parsed.flags, VDE_FLAG_HAS_ATTRIBUTES | VDE_FLAG_HAS_HISTORY); EXPECT_EQ(parsed.data_offset, 64u); EXPECT_TRUE(parsed.is_valid()); } TEST(VdeHeaderTest, InvalidMagicThrows) { uint8_t bad[32] = {}; bad[0] = 'B'; bad[1] = 'A'; bad[2] = 'D'; bad[3] = '!'; VdeHeader hdr = VdeHeader::parse(bad, 32); EXPECT_FALSE(hdr.is_valid()); } TEST(VdeHeaderTest, TooSmallThrows) { uint8_t tiny[16] = {}; EXPECT_THROW(VdeHeader::parse(tiny, 16), std::runtime_error); } // ═══════════════════════════════════════════════════════════════ // 2. 基本保存与加载 // ═══════════════════════════════════════════════════════════════ TEST(VdeFormat, SaveBoxToVde) { auto box = make_test_box(); const char* path = "/tmp/test_box.vde"; clean(path); ASSERT_TRUE(save_vde(box, path)); std::ifstream f(path, std::ios::binary); ASSERT_TRUE(f.good()); // 检查 magic bytes "VDE1" (little-endian uint32 0x31454456) char magic[4]; f.read(magic, 4); EXPECT_EQ(magic[0], 'V'); EXPECT_EQ(magic[1], 'D'); EXPECT_EQ(magic[2], 'E'); EXPECT_EQ(magic[3], '1'); clean(path); } TEST(VdeFormat, LoadBoxFromVde) { auto box = make_test_box(); const char* path = "/tmp/test_load.vde"; clean(path); ASSERT_TRUE(save_vde(box, path)); auto loaded = load_vde(path); expect_valid_body(loaded); // 拓扑计数应一致 EXPECT_EQ(loaded.num_vertices(), box.num_vertices()); EXPECT_EQ(loaded.num_edges(), box.num_edges()); EXPECT_EQ(loaded.num_faces(), box.num_faces()); EXPECT_EQ(loaded.num_bodies(), box.num_bodies()); clean(path); } TEST(VdeFormat, CylinderRoundtrip) { auto cyl = make_test_cylinder(); const char* path = "/tmp/test_cyl.vde"; clean(path); ASSERT_TRUE(save_vde(cyl, path)); auto loaded = load_vde(path); expect_valid_body(loaded); EXPECT_EQ(loaded.num_vertices(), cyl.num_vertices()); EXPECT_EQ(loaded.num_faces(), cyl.num_faces()); EXPECT_EQ(loaded.num_surfaces(), cyl.num_surfaces()); clean(path); } TEST(VdeFormat, NonexistentFileThrows) { EXPECT_THROW(load_vde("/tmp/nonexistent_vde_file.vde"), std::runtime_error); } // ═══════════════════════════════════════════════════════════════ // 3. JSON 导出测试 // ═══════════════════════════════════════════════════════════════ TEST(VdeJson, SaveJsonFile) { auto box = make_test_box(); const char* path = "/tmp/test_box.vde.json"; clean(path); ASSERT_TRUE(save_vde_json(box, path)); std::ifstream f(path); ASSERT_TRUE(f.good()); std::string content((std::istreambuf_iterator(f)), {}); // JSON 应包含关键字段 EXPECT_NE(content.find("\"format\": \"VDE1\""), std::string::npos); EXPECT_NE(content.find("\"vertices\""), std::string::npos); EXPECT_NE(content.find("\"edges\""), std::string::npos); EXPECT_NE(content.find("\"faces\""), std::string::npos); EXPECT_NE(content.find("\"loops\""), std::string::npos); EXPECT_NE(content.find("\"surfaces\""), std::string::npos); EXPECT_NE(content.find("\"tolerance\""), std::string::npos); clean(path); } TEST(VdeJson, JsonContainsVertexData) { auto box = make_test_box(); const char* path = "/tmp/test_json_vertices.vde.json"; clean(path); save_vde_json(box, path); std::ifstream f(path); std::string content((std::istreambuf_iterator(f)), {}); // 正方体应有 8 个顶点 EXPECT_NE(content.find("\"point\""), std::string::npos); EXPECT_NE(content.find("\"tolerance\""), std::string::npos); clean(path); } // ═══════════════════════════════════════════════════════════════ // 4. 容差测试 // ═══════════════════════════════════════════════════════════════ TEST(VdeTolerance, ToleranceConfigRoundtrip) { // 设置非默认容差 ToleranceConfig orig; orig.vertex_merge = 1e-5; orig.edge_merge = 2e-5; orig.boolean = 3e-5; ToleranceConfig::set_global(orig); auto box = make_test_box(); const char* path = "/tmp/test_tol.vde"; clean(path); save_vde(box, path); auto loaded = load_vde(path); const auto& restored = ToleranceConfig::global(); EXPECT_DOUBLE_EQ(restored.vertex_merge, orig.vertex_merge); EXPECT_DOUBLE_EQ(restored.edge_merge, orig.edge_merge); EXPECT_DOUBLE_EQ(restored.boolean, orig.boolean); // 恢复默认 ToleranceConfig::set_global(ToleranceConfig{}); clean(path); } // ═══════════════════════════════════════════════════════════════ // 5. 属性测试 // ═══════════════════════════════════════════════════════════════ TEST(VdeAttributes, SaveWithVertexAttributes) { auto box = make_test_box(); VdeAttributes attrs; attrs.vertex_attrs.push_back({0, "corner_0", 0xFF0000FF, "key=value"}); attrs.vertex_attrs.push_back({1, "corner_1", 0x00FF00FF, ""}); const char* path = "/tmp/test_attrs.vde"; clean(path); ASSERT_TRUE(save_vde(box, path, attrs)); // 重新加载并检查属性 VdeAttributes loaded_attrs; load_vde_with_attrs(path, loaded_attrs); ASSERT_EQ(loaded_attrs.vertex_attrs.size(), 2u); EXPECT_EQ(loaded_attrs.vertex_attrs[0].vertex_id, 0); EXPECT_EQ(loaded_attrs.vertex_attrs[0].name, "corner_0"); EXPECT_EQ(loaded_attrs.vertex_attrs[0].color_rgba, 0xFF0000FFu); EXPECT_EQ(loaded_attrs.vertex_attrs[0].custom_data, "key=value"); EXPECT_EQ(loaded_attrs.vertex_attrs[1].vertex_id, 1); clean(path); } TEST(VdeAttributes, SaveWithBodyAttributes) { auto box = make_test_box(); VdeAttributes attrs; attrs.body_attrs.push_back({0, "MyPart", "material=steel"}); const char* path = "/tmp/test_body_attrs.vde"; clean(path); save_vde(box, path, attrs); VdeAttributes loaded_attrs; load_vde_with_attrs(path, loaded_attrs); ASSERT_EQ(loaded_attrs.body_attrs.size(), 1u); EXPECT_EQ(loaded_attrs.body_attrs[0].name, "MyPart"); EXPECT_EQ(loaded_attrs.body_attrs[0].custom_data, "material=steel"); clean(path); } // ═══════════════════════════════════════════════════════════════ // 6. 建模历史测试 // ═══════════════════════════════════════════════════════════════ TEST(VdeHistory, SaveWithFeatureHistory) { auto box = make_test_box(); std::vector history; FeatureNode root; root.type = FeatureType::PrimitiveBox; root.params.values = {2.0, 3.0, 4.0}; root.params.name = "Box"; history.push_back(std::move(root)); const char* path = "/tmp/test_hist.vde"; clean(path); ASSERT_TRUE(save_vde(box, path, {}, &history)); // 检查文件大小合理 std::ifstream f(path, std::ios::binary | std::ios::ate); ASSERT_TRUE(f.good()); EXPECT_GT(f.tellg(), std::streampos(64)); // 加载应该成功(历史在 load_vde 中被静默跳过) auto loaded = load_vde(path); expect_valid_body(loaded); clean(path); } // ═══════════════════════════════════════════════════════════════ // 7. 序列化缓冲区往返测试 // ═══════════════════════════════════════════════════════════════ TEST(VdeSerialize, BufferRoundtrip) { auto box = make_test_box(); auto data = serialize_vde(box); EXPECT_GT(data.size(), 32u); auto restored = deserialize_vde(data); expect_valid_body(restored); EXPECT_EQ(restored.num_vertices(), box.num_vertices()); EXPECT_EQ(restored.num_faces(), box.num_faces()); } TEST(VdeSerialize, EmptyModelBuffer) { BrepModel empty; auto data = serialize_vde(empty); EXPECT_GE(data.size(), 32u); auto restored = deserialize_vde(data); EXPECT_EQ(restored.num_vertices(), 0u); } // ═══════════════════════════════════════════════════════════════ // 8. 文件格式验证 // ═══════════════════════════════════════════════════════════════ TEST(VdeFormatValidation, FileHasCorrectMagic) { auto box = make_test_box(); const char* path = "/tmp/test_magic.vde"; clean(path); save_vde(box, path); std::ifstream f(path, std::ios::binary); char magic[4]; f.read(magic, 4); EXPECT_EQ(std::string(magic, 4), "VDE1"); clean(path); } TEST(VdeFormatValidation, FileHasSections) { auto box = make_test_box(); const char* path = "/tmp/test_sections.vde"; clean(path); save_vde(box, path); std::ifstream f(path, std::ios::binary | std::ios::ate); auto sz = f.tellg(); f.seekg(0); std::vector data(static_cast(sz)); f.read(reinterpret_cast(data.data()), sz); // 头部后应至少有 拓扑 + 几何 + 容差 三个段 EXPECT_GT(data.size(), 32u + 30u); // header + 3 section headers minimum clean(path); } // ═══════════════════════════════════════════════════════════════ // 9. 边界情况 // ═══════════════════════════════════════════════════════════════ TEST(VdeEdgeCase, VerySmallFileThrows) { std::vector tiny(8, 0); EXPECT_THROW(deserialize_vde(tiny), std::runtime_error); } TEST(VdeEdgeCase, CorruptedMagicThrows) { auto box = make_test_box(); auto data = serialize_vde(box); // 损坏 magic data[0] = 'X'; data[1] = 'X'; data[2] = 'X'; data[3] = 'X'; EXPECT_THROW(deserialize_vde(data), std::runtime_error); }