7b76689ea1
v11.1 — Test Infrastructure (14 files): - .github/workflows/ci.yml: Ubuntu 22.04 + GCC 11, auto ctest on push - tests/regression/: degenerate geometry, extreme coords, thin wall, large models - tests/fuzz/: SDF random CSG, random booleans, format round-trip, continuous mode - tests/benchmark/: boolean perf, MC perf, IO perf benchmarks - docs/benchmark-report.md: benchmark template v11.2 — Code Quality + Docs: - .clang-tidy: 15 check categories, 22 exclusions - .github/workflows/static-analysis.yml: clang-tidy CI scan - CODEOWNERS, SECURITY.md - docs: API overview, testing guide, contributing guide - README: module capability overview table Pending: binary formats + curve projection (retrying)
226 lines
5.7 KiB
C++
226 lines
5.7 KiB
C++
/// @file regression_extreme.cpp
|
||
/// @brief 极端坐标回归测试
|
||
///
|
||
/// 覆盖场景:
|
||
/// 1. 极小坐标(1e-15)
|
||
/// 2. 极大坐标(1e15)
|
||
/// 3. NaN 输入
|
||
/// 4. Inf 输入
|
||
///
|
||
/// 每个测试:构造极端体 → 布尔/修复/验证 → 断言不崩溃
|
||
|
||
#include <gtest/gtest.h>
|
||
#include "vde/brep/brep.h"
|
||
#include "vde/brep/modeling.h"
|
||
#include "vde/brep/brep_boolean.h"
|
||
#include "vde/brep/brep_validate.h"
|
||
#include "vde/brep/brep_heal.h"
|
||
#include "vde/core/point.h"
|
||
#include <cmath>
|
||
#include <limits>
|
||
|
||
using namespace vde::brep;
|
||
using namespace vde::core;
|
||
|
||
// ── 极小坐标测试 ──
|
||
|
||
TEST(RegressionExtreme, TinyCoordinates_1eMinus15) {
|
||
// 极小的盒子:尺寸为 1e-15
|
||
double s = 1e-15;
|
||
EXPECT_NO_THROW({
|
||
auto tiny = make_box(s, s, s);
|
||
// 验证合法性
|
||
EXPECT_TRUE(tiny.is_valid());
|
||
|
||
// 布尔运算:与正常盒子
|
||
auto box = make_box(1.0, 1.0, 1.0);
|
||
auto u = brep_union(tiny, box);
|
||
EXPECT_TRUE(u.is_valid());
|
||
auto i = brep_intersection(tiny, box);
|
||
EXPECT_TRUE(i.is_valid());
|
||
|
||
// 导出网格
|
||
auto mesh = tiny.to_mesh();
|
||
(void)mesh;
|
||
});
|
||
}
|
||
|
||
TEST(RegressionExtreme, TinyCoordinates_1eMinus10) {
|
||
double s = 1e-10;
|
||
EXPECT_NO_THROW({
|
||
auto tiny = make_box(s, s, s);
|
||
EXPECT_TRUE(tiny.is_valid());
|
||
auto mesh = tiny.to_mesh();
|
||
(void)mesh;
|
||
|
||
// 修复流水线
|
||
BrepModel copy = tiny;
|
||
heal_topology(copy, s * 0.1);
|
||
});
|
||
}
|
||
|
||
TEST(RegressionExtreme, TinyCoordinates_Mixed) {
|
||
// 一个维度极小,其他正常
|
||
EXPECT_NO_THROW({
|
||
auto thin = make_box(1e-15, 1.0, 1.0);
|
||
EXPECT_NO_THROW({ thin.is_valid(); });
|
||
auto result = validate(thin);
|
||
(void)result;
|
||
|
||
auto mesh = thin.to_mesh();
|
||
(void)mesh;
|
||
});
|
||
}
|
||
|
||
// ── 极大坐标测试 ──
|
||
|
||
TEST(RegressionExtreme, HugeCoordinates_1e15) {
|
||
double s = 1e15;
|
||
EXPECT_NO_THROW({
|
||
auto huge = make_box(s, s, s);
|
||
// 基本验证不崩溃
|
||
EXPECT_TRUE(huge.is_valid());
|
||
|
||
// 包围盒计算
|
||
auto bb = huge.bounds();
|
||
EXPECT_TRUE(std::isfinite(bb.min().x()));
|
||
EXPECT_TRUE(std::isfinite(bb.max().x()));
|
||
|
||
// 布尔自交
|
||
auto u = brep_union(huge, huge);
|
||
EXPECT_TRUE(u.is_valid());
|
||
|
||
// 网格导出(大尺寸模型)
|
||
auto mesh = huge.to_mesh(0.01 * s);
|
||
(void)mesh;
|
||
});
|
||
}
|
||
|
||
TEST(RegressionExtreme, HugeVsTiny) {
|
||
// 巨大体与极小体的布尔
|
||
EXPECT_NO_THROW({
|
||
auto huge = make_box(1e12, 1e12, 1e12);
|
||
auto tiny = make_box(1e-12, 1e-12, 1e-12);
|
||
|
||
auto u = brep_union(huge, tiny);
|
||
EXPECT_TRUE(u.is_valid());
|
||
auto i = brep_intersection(huge, tiny);
|
||
EXPECT_TRUE(i.is_valid());
|
||
});
|
||
}
|
||
|
||
// ── NaN 输入测试 ──
|
||
|
||
TEST(RegressionExtreme, NaN_Handling) {
|
||
double nan = std::numeric_limits<double>::quiet_NaN();
|
||
|
||
// make_box 应对 NaN 表现得优雅(返回无效体或抛出,但绝不能崩溃)
|
||
EXPECT_NO_THROW({
|
||
auto body = make_box(nan, 1.0, 1.0);
|
||
(void)body;
|
||
});
|
||
|
||
EXPECT_NO_THROW({
|
||
auto body = make_box(1.0, nan, 1.0);
|
||
(void)body;
|
||
});
|
||
|
||
EXPECT_NO_THROW({
|
||
auto body = make_box(1.0, 1.0, nan);
|
||
(void)body;
|
||
});
|
||
|
||
EXPECT_NO_THROW({
|
||
auto body = make_box(nan, nan, nan);
|
||
(void)body;
|
||
});
|
||
}
|
||
|
||
TEST(RegressionExtreme, NaN_BooleanResilience) {
|
||
// 若 NaN 产生了有效的 body,检查布尔运算不崩溃
|
||
auto good = make_box(2.0, 2.0, 2.0);
|
||
|
||
// 将所有尝试包裹在 EXPECT_NO_THROW 中
|
||
EXPECT_NO_THROW({
|
||
try {
|
||
auto bad = make_box(std::numeric_limits<double>::quiet_NaN(), 1.0, 1.0);
|
||
auto u = brep_union(good, bad);
|
||
(void)u;
|
||
} catch (...) {
|
||
// 允许抛异常但不应崩溃
|
||
}
|
||
});
|
||
}
|
||
|
||
// ── Inf 输入测试 ──
|
||
|
||
TEST(RegressionExtreme, Inf_Handling) {
|
||
double inf = std::numeric_limits<double>::infinity();
|
||
|
||
EXPECT_NO_THROW({
|
||
auto body = make_box(inf, 1.0, 1.0);
|
||
(void)body;
|
||
});
|
||
|
||
EXPECT_NO_THROW({
|
||
auto body = make_box(1.0, inf, 1.0);
|
||
(void)body;
|
||
});
|
||
|
||
EXPECT_NO_THROW({
|
||
auto body = make_box(1.0, 1.0, inf);
|
||
(void)body;
|
||
});
|
||
}
|
||
|
||
// ── 极大值 vs 极小值布尔 ──
|
||
|
||
TEST(RegressionExtreme, MaxDouble_Boolean) {
|
||
double big = 1e100;
|
||
double small = 1e-100;
|
||
|
||
EXPECT_NO_THROW({
|
||
auto a = make_box(big, 1.0, 1.0);
|
||
auto b = make_box(small, 1.0, 1.0);
|
||
auto u = brep_union(a, b);
|
||
(void)u.is_valid();
|
||
});
|
||
}
|
||
|
||
// ── 边界值批处理 ──
|
||
|
||
TEST(RegressionExtreme, BoundaryValuesBatch) {
|
||
// 一系列边界值,确保每个都不会崩溃
|
||
double values[] = {
|
||
0.0,
|
||
std::numeric_limits<double>::min(),
|
||
std::numeric_limits<double>::denorm_min(),
|
||
std::numeric_limits<double>::epsilon(),
|
||
1e-15, 1e-10, 1e-5,
|
||
1.0, 10.0, 100.0,
|
||
1e5, 1e10, 1e15,
|
||
std::numeric_limits<double>::max() * 0.5,
|
||
};
|
||
|
||
for (double v : values) {
|
||
if (!std::isfinite(v)) continue;
|
||
EXPECT_NO_THROW({
|
||
auto body = make_box(v, v, v);
|
||
body.is_valid();
|
||
auto mesh = body.to_mesh();
|
||
(void)mesh;
|
||
}) << "failed at v=" << v;
|
||
}
|
||
|
||
// 混合边界值布尔
|
||
auto a = make_box(values[0], 1.0, 1.0);
|
||
for (int i = 1; i < 10; ++i) {
|
||
if (!std::isfinite(values[i])) continue;
|
||
EXPECT_NO_THROW({
|
||
auto b = make_box(values[i], 1.0, 1.0);
|
||
auto u = brep_union(a, b);
|
||
(void)u.is_valid();
|
||
}) << "failed at i=" << i;
|
||
}
|
||
}
|