/** * @file test_boolean_fallback.cpp * @brief 布尔策略回退阶梯 单元测试 * * 覆盖场景: * 1. Level 1 正常成功(ssi_boolean 直接返回) * 2. Level 2 容差回退触发 * 3. Level 3 网格回退触发 * 4. Level 4 SDF 兜底 * 5. 诊断信息验证(levels_tried, diagnostics) * 6. brep_quality 标记 * 7. 空体/退化输入 * 8. summary() 输出 * 9. 三操差异(Union/Intersection/Difference) * 10. 异常安全(不崩溃) */ #include #include "vde/brep/brep.h" #include "vde/brep/modeling.h" #include "vde/brep/boolean_fallback.h" #include "vde/brep/ssi_boolean.h" #include "vde/brep/tolerant_modeling.h" using namespace vde::brep; using namespace vde::core; // ═══════════════════════════════════════════════════════════ // Test 1: Level 1 正常成功 — 两个简单立方体 Union // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, Level1_Success_SimpleUnion) { auto box1 = make_box(2.0, 2.0, 2.0); auto box2 = make_box(2.0, 2.0, 2.0); auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union); EXPECT_EQ(result.final_level, 1); EXPECT_TRUE(result.brep_quality); EXPECT_GT(result.result_body.num_vertices(), 0u); EXPECT_GT(result.result_body.num_faces(), 0u); EXPECT_TRUE(result.brep_body.has_value()); EXPECT_EQ(result.levels_tried.size(), 1u); EXPECT_EQ(result.levels_tried[0], 1); } // ═══════════════════════════════════════════════════════════ // Test 2: Level 1 正常成功 — Intersection // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, Level1_Success_Intersection) { auto box1 = make_box(4.0, 4.0, 4.0); auto box2 = make_box(4.0, 4.0, 4.0); auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Intersection); EXPECT_EQ(result.final_level, 1); EXPECT_TRUE(result.brep_quality); EXPECT_GT(result.result_body.num_vertices(), 0u); } // ═══════════════════════════════════════════════════════════ // Test 3: Level 1 正常成功 — Difference // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, Level1_Success_Difference) { auto big = make_box(4.0, 4.0, 4.0); auto small = make_box(2.0, 2.0, 2.0); auto result = brep_boolean_robust(big, small, FallbackBoolOp::Difference); EXPECT_EQ(result.final_level, 1); EXPECT_TRUE(result.brep_quality); EXPECT_GT(result.result_body.num_faces(), 0u); } // ═══════════════════════════════════════════════════════════ // Test 4: 诊断信息验证 — levels_tried + diagnostics 数量一致 // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, Diagnostics_Tracking) { auto box1 = make_box(2.0, 2.0, 2.0); auto box2 = make_box(2.0, 2.0, 2.0); auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union); // levels_tried 和 level_diagnostics 数量必须一致 EXPECT_EQ(result.levels_tried.size(), result.level_diagnostics.size()); // Level 1 成功时,只尝试了 1 级 EXPECT_EQ(result.levels_tried.size(), 1u); EXPECT_EQ(result.final_level, 1); // final_level 必须在 levels_tried 中 bool found = false; for (int lv : result.levels_tried) { if (lv == result.final_level) { found = true; break; } } EXPECT_TRUE(found); } // ═══════════════════════════════════════════════════════════ // Test 5: brep_quality 标记 — Level 1/2 为 true // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, BRepQuality_Flag_Level1) { auto box1 = make_box(2.0, 2.0, 2.0); auto box2 = make_box(2.0, 2.0, 2.0); auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union); if (result.final_level <= 2) { EXPECT_TRUE(result.brep_quality); EXPECT_TRUE(result.brep_body.has_value()); } else { EXPECT_FALSE(result.brep_quality); } } // ═══════════════════════════════════════════════════════════ // Test 6: summary() 输出非空 // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, Summary_ProducesOutput) { auto box1 = make_box(2.0, 2.0, 2.0); auto box2 = make_box(2.0, 2.0, 2.0); auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union); auto s = result.summary(); EXPECT_FALSE(s.empty()); EXPECT_NE(s.find("BooleanFallbackResult"), std::string::npos); EXPECT_NE(s.find("final_level"), std::string::npos); EXPECT_NE(s.find("levels_tried"), std::string::npos); } // ═══════════════════════════════════════════════════════════ // Test 7: 空体输入不崩溃 // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, EmptyBody_DoesNotCrash) { BrepModel empty_a; BrepModel empty_b; auto box = make_box(2.0, 2.0, 2.0); // 两个空体 auto r1 = brep_boolean_robust(empty_a, empty_b, FallbackBoolOp::Union); EXPECT_NO_THROW({ auto s = r1.summary(); (void)s; }); // 一个空体 + 正常体 auto r2 = brep_boolean_robust(empty_a, box, FallbackBoolOp::Union); EXPECT_NO_THROW({ auto s = r2.summary(); (void)s; }); auto r3 = brep_boolean_robust(box, empty_a, FallbackBoolOp::Intersection); EXPECT_NO_THROW({ auto s = r3.summary(); (void)s; }); } // ═══════════════════════════════════════════════════════════ // Test 8: 每级内部函数独立可调 // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, LevelFunctions_IndependentlyCallable) { auto box1 = make_box(2.0, 2.0, 2.0); auto box2 = make_box(3.0, 3.0, 3.0); // Level 1: try_ssi_boolean { auto [diag, ok] = try_ssi_boolean(box1, box2, FallbackBoolOp::Union); EXPECT_TRUE(ok); EXPECT_TRUE(diag.failed_faces.empty()); } // Level 2: try_tolerant_boolean { auto [diag, ok] = try_tolerant_boolean(box1, box2, FallbackBoolOp::Union); EXPECT_TRUE(ok); EXPECT_TRUE(diag.failed_faces.empty()); } // Level 3: try_mesh_boolean_fallback { auto [mesh, ok] = try_mesh_boolean_fallback(box1, box2, FallbackBoolOp::Union); EXPECT_TRUE(ok); EXPECT_GT(mesh.num_vertices(), 0u); } // Level 4: try_sdf_boolean_fallback (永不失败) { auto mesh = try_sdf_boolean_fallback(box1, box2, FallbackBoolOp::Union); EXPECT_GT(mesh.num_vertices(), 0u); } } // ═══════════════════════════════════════════════════════════ // Test 9: 三操作都支持(Union / Intersection / Difference) // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, AllThreeOperations_Supported) { auto box1 = make_box(2.0, 2.0, 2.0); auto box2 = make_box(3.0, 3.0, 3.0); auto r_union = brep_boolean_robust(box1, box2, FallbackBoolOp::Union); EXPECT_GT(r_union.result_body.num_vertices(), 0u); auto r_inter = brep_boolean_robust(box1, box2, FallbackBoolOp::Intersection); EXPECT_GT(r_inter.result_body.num_vertices(), 0u); auto r_diff = brep_boolean_robust(box2, box1, FallbackBoolOp::Difference); EXPECT_GT(r_diff.result_body.num_vertices(), 0u); } // ═══════════════════════════════════════════════════════════ // Test 10: 重叠立方体 — 各级都能产生有效结果 // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, OverlappingBoxes_AllLevelsProduceValidMesh) { auto box1 = make_box(2.0, 2.0, 2.0); auto box2 = make_box(2.0, 2.0, 2.0); auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union); // 无论哪一级成功,结果网格必须有效 EXPECT_GT(result.result_body.num_vertices(), 0u); EXPECT_GT(result.result_body.num_faces(), 0u); // final_level 在 1-4 范围内 EXPECT_GE(result.final_level, 1); EXPECT_LE(result.final_level, 4); // 诊断链非空 EXPECT_FALSE(result.level_diagnostics.empty()); } // ═══════════════════════════════════════════════════════════ // Test 11: SDF Level 4 单独验证(盒+球并集) // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, SDF_Level4_BoxWithSphere) { auto box = make_box(3.0, 3.0, 3.0); auto sphere = make_sphere(1.5); auto mesh = try_sdf_boolean_fallback(box, sphere, FallbackBoolOp::Union); EXPECT_GT(mesh.num_vertices(), 0u); EXPECT_GT(mesh.num_faces(), 0u); } // ═══════════════════════════════════════════════════════════ // Test 12: 异常安全 — 不崩溃 // ═══════════════════════════════════════════════════════════ TEST(BooleanFallbackTest, ExceptionSafety_AllOperations) { auto box = make_box(2.0, 2.0, 2.0); auto sphere = make_sphere(1.0); // 重复相同操作不应该崩溃 for (int i = 0; i < 3; ++i) { EXPECT_NO_THROW({ auto r = brep_boolean_robust(box, sphere, FallbackBoolOp::Union); auto s = r.summary(); (void)s; }); } }