# 测试指南 ViewDesignEngine 使用 Google Test 框架,通过 CTest 运行测试。 ## 目录 1. [测试架构](#1-测试架构) 2. [运行测试](#2-运行测试) 3. [编写测试](#3-编写测试) 4. [测试规范](#4-测试规范) 5. [Sanitizer 测试](#5-sanitizer-测试) 6. [性能基准](#6-性能基准) 7. [模糊测试](#7-模糊测试) 8. [CI 集成](#8-ci-集成) --- ## 1. 测试架构 ``` tests/ ├── CMakeLists.txt # 顶层测试 CMake ├── foundation/ # 基础设施测试 │ ├── CMakeLists.txt │ └── test_io_obj.cpp ├── core/ # 核心模块测试 │ ├── CMakeLists.txt │ ├── test_polygon.cpp │ ├── test_distance.cpp │ └── test_cam_5axis.cpp ├── curves/ # 曲线曲面测试 ├── mesh/ # 网格处理测试 │ ├── CMakeLists.txt │ ├── test_fea_mesh.cpp │ ├── test_reverse_engineering.cpp │ └── test_xxx.cpp ├── spatial/ # 空间索引测试 ├── boolean/ # 布尔运算测试 ├── collision/ # 碰撞检测测试 ├── brep/ # B-Rep 测试 ├── sdf/ # SDF 测试 ├── sketch/ # 草图约束测试 ├── gpu/ # GPU 测试 └── fuzz/ # 模糊测试 ``` ### 测试数量 | 模块 | 测试数 | 状态 | |------|--------|------| | foundation | ~30 | ✅ | | core | ~40 | ✅ | | curves | ~50 | ✅ | | mesh | ~60 | ✅ | | spatial | ~30 | ✅ | | boolean | ~40 | ✅ | | collision | ~30 | ✅ | | brep | ~70 | ✅ | | sdf | 189 | ✅ | | sketch | ~10 | ✅ | | gpu | ~5 | ✅ | | **合计** | **~500+** | ✅ | ## 2. 运行测试 ### 基础命令 ```bash # 构建并运行所有测试 cmake -B build -DBUILD_TESTS=ON cmake --build build -j$(nproc) cd build && ctest --output-on-failure # 并行运行 ctest -j$(nproc) --output-on-failure # 详细输出 ctest -V # 运行特定测试 ctest -R test_brep # 名称匹配 ctest -R "test_brep|test_sdf" # 多个模式 # 排除特定测试 ctest -E test_fuzz ``` ### 直接运行可执行文件 ```bash # 运行单个测试文件 ./build/tests/brep/test_brep # 运行特定测试用例 ./build/tests/brep/test_brep --gtest_filter="*Box*" # 列出所有测试用例 ./build/tests/brep/test_brep --gtest_list_tests # 重复运行(检测不稳定性) ./build/tests/mesh/test_fea_mesh --gtest_repeat=100 ``` ### Docker 中运行 ```bash docker run --rm --cpus=4 --memory=8g \ -v $(pwd):/ws vde-builder bash -c \ "cd /ws/build && ctest -j4 --output-on-failure" ``` ## 3. 编写测试 ### 3.0 如何添加新测试(完整步骤) 向 ViewDesignEngine 添加新测试遵循以下标准工作流: **步骤 1: 创建测试文件** 在对应模块的测试目录下创建 `test_.cpp`: ```bash # 例如:为 brep 模块的新功能添加测试 touch tests/brep/test_new_feature.cpp ``` **步骤 2: 编写测试代码** ```cpp // tests/brep/test_new_feature.cpp #include #include "vde/brep/modeling.h" #include "vde/brep/new_feature.h" // 被测头文件 using namespace vde::brep; using namespace vde::core; // 基本功能测试 TEST(NewFeatureTest, BasicOperation) { auto box = make_box(2, 2, 2); auto result = new_feature_operation(box, /* params */); EXPECT_TRUE(result.is_valid()); } // 边界条件测试 TEST(NewFeatureTest, EmptyInput) { BrepModel empty; EXPECT_THROW(new_feature_operation(empty), std::invalid_argument); } // 数值精度测试 TEST(NewFeatureTest, NumericalPrecision) { auto sphere = make_sphere(1.0); auto result = new_feature_operation(sphere, /* params */); EXPECT_NEAR(result.volume(), 4.18879, 1e-5); } ``` **步骤 3: 注册到 CMake** 编辑 `tests//CMakeLists.txt`,使用标准宏: ```cmake # tests/brep/CMakeLists.txt # 方式 A:使用 add_vde_test 宏(自动链接聚合库 vde) add_vde_test(test_new_feature) # 方式 B:如需精确控制链接(避免聚合库中其他模块的预存错误) add_executable(test_new_feature test_new_feature.cpp) target_link_libraries(test_new_feature PRIVATE vde_brep vde_curves vde_core vde_foundation GTest::gtest GTest::gtest_main ) add_test(NAME test_new_feature COMMAND test_new_feature) ``` **步骤 4: 本地验证** ```bash # 配置构建 cmake -B build -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release # 只编译新测试目标 cmake --build build --target test_new_feature -j$(nproc) # 运行新测试 ./build/tests/brep/test_new_feature # 运行特定用例 ./build/tests/brep/test_new_feature --gtest_filter="*BasicOperation*" # 确认不破坏已有测试 cd build && ctest --output-on-failure -j$(nproc) ``` **步骤 5: Docker 环境验证(推荐)** ```bash docker run --rm --cpus=2 --memory=8g \ -v /root/workspace:/ws -w /ws/ViewDesignEngine \ vde-builder:latest bash -c " cmake -B build_verify -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build_verify --target test_new_feature -j2 && ./build_verify/tests/brep/test_new_feature " ``` ### 3.1 基本模板 ```cpp // tests/mesh/test_my_feature.cpp #include #include #include using namespace vde::core; using namespace vde::mesh; // 基本断言 TEST(MyFeatureTest, CreateEmptyMesh) { HalfedgeMesh mesh; EXPECT_EQ(mesh.vertex_count(), 0); EXPECT_EQ(mesh.face_count(), 0); } // 带 fixture 的测试 class MyFeatureFixture : public ::testing::Test { protected: void SetUp() override { mesh.add_vertex(Point3D(0, 0, 0)); mesh.add_vertex(Point3D(1, 0, 0)); mesh.add_vertex(Point3D(0, 1, 0)); mesh.add_face({0, 1, 2}); } HalfedgeMesh mesh; }; TEST_F(MyFeatureFixture, FaceCount) { EXPECT_EQ(mesh.face_count(), 1); } TEST_F(MyFeatureFixture, VertexPositions) { EXPECT_NEAR(mesh.vertex(0).x, 0.0, 1e-6); EXPECT_NEAR(mesh.vertex(1).x, 1.0, 1e-6); } ``` ### 3.2 参数化测试 ```cpp // 使用不同参数重复测试 class BooleanOpTest : public ::testing::TestWithParam< std::tuple> {}; TEST_P(BooleanOpTest, ConsistentResult) { auto [op_name, n1, n2] = GetParam(); // ... test logic using parameters ... } INSTANTIATE_TEST_SUITE_P( MeshBoolean, BooleanOpTest, ::testing::Values( std::make_tuple("union", 100, 50), std::make_tuple("intersection", 200, 100), std::make_tuple("difference", 300, 150) )); ``` ### 3.3 注册到 CMake ```cmake # tests/mesh/CMakeLists.txt add_executable(test_my_feature test_my_feature.cpp) target_link_libraries(test_my_feature PRIVATE vde_mesh vde_core PRIVATE GTest::gtest GTest::gtest_main ) add_test(NAME test_my_feature COMMAND test_my_feature) ``` ## 4. 测试规范 ### 命名规范 ``` 测试文件名: test_.cpp 测试套件名: Test 或 Test 测试用例名: 示例: - TEST(BrepValidateTest, ClosedShellPassesValidation) - TEST(MeshSimplifyTest, SimplifyingCubeReducesFaceCount) ``` ### 断言选择 | 场景 | 推荐断言 | |------|---------| | 相等比较 | `EXPECT_EQ(a, b)` | | 浮点比较 | `EXPECT_NEAR(a, b, tolerance)` | | 布尔检查 | `EXPECT_TRUE(condition)` | | 非致命错误 | `EXPECT_*` 继续执行 | | 致命错误 | `ASSERT_*` 立即终止 | | 异常检查 | `EXPECT_THROW(expr, exception_type)` | ### 测试清单 每个功能模块的测试应覆盖: - [ ] **正常路径** (Happy Path) - [ ] **边界条件** (空输入、极值、零值) - [ ] **错误处理** (无效输入、超范围) - [ ] **退化情况** (共线/共面点、零面积面) - [ ] **数值稳定性** (接近容差的场景) - [ ] **性能回归** (大规模输入不超时) ### 反模式 ```cpp // ❌ 测试依赖全局状态 static int counter = 0; TEST(Foo, Bar) { EXPECT_EQ(counter++, 0); } // 不可重复 // ❌ 测试依赖执行顺序 TEST(Foo, A) { /* 设置全局状态 */ } TEST(Foo, B) { /* 依赖 A 的状态 */ } // ❌ 过于宽松的容差 EXPECT_NEAR(result, expected, 100.0); // 容差过大 // ❌ 没有断言(不验证任何东西) TEST(Foo, Bar) { auto x = compute(); // 没有 EXPECT/ASSERT } ``` ## 5. Sanitizer 测试 ### Address Sanitizer (ASan) ```bash # 构建 ASan 版本 cmake -B build_asan \ -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_SANITIZERS=ON \ -DVDE_USE_OPENMP=OFF cmake --build build_asan -j$(nproc) # 运行测试 cd build_asan ASAN_OPTIONS=detect_leaks=1:abort_on_error=1 \ ctest --output-on-failure -j4 ``` ### Undefined Behavior Sanitizer (UBSan) ```bash # 手动添加 UBSan 标志 cmake -B build_ubsan \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-fsanitize=undefined" cmake --build build_ubsan -j$(nproc) cd build_ubsan && ctest --output-on-failure ``` ### Thread Sanitizer (TSan) ```bash cmake -B build_tsan \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-fsanitize=thread" \ -DVDE_USE_OPENMP=OFF cmake --build build_tsan -j$(nproc) cd build_tsan && ctest --output-on-failure ``` ## 6. 性能基准 ### 运行基准测试 ```bash # 构建基准测试 cmake -B build_bench -DBUILD_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release cmake --build build_bench -j$(nproc) # 运行所有基准 cd build_bench && ctest -R bench_ # 直接运行并查看详细报告 ./build_bench/bench/bench_bvh --benchmark_format=json ``` ### 基准模块 | 基准 | 内容 | |------|------| | `bench_bvh` | SAH 构建 (N=1K/10K/100K) + 射线查询 | | `bench_delaunay` | 2D Bowyer-Watson (N=100/1K/10K) | | `bench_boolean` | 3D 网格布尔 (N=100/500) | | `bench_curves` | Bezier/BSpline evaluate 吞吐量 | | `bench_spatial` | R-Tree/KD-Tree 构建 + kNN | ## 7. 模糊测试 ```bash # 构建并运行模糊测试 cmake -B build -DBUILD_TESTS=ON cmake --build build -j$(nproc) # 运行模糊测试 ./build/tests/fuzz/test_fuzz_sdf ./build/tests/fuzz/test_fuzz_boolean ./build/tests/fuzz/test_fuzz_format ``` 模糊测试覆盖的模块: - **SDF**: 随机 CSG 树组合,检测 NaN/崩溃 - **Boolean**: 随机网格布尔运算,检测拓扑错误 - **Format**: 随机几何 → 导出 → 导入 → 验证一致性 ## 8. CI 集成 VDE 使用 Gitea CI 自动运行测试。 ### CI 配置文件 `.gitea/workflows/build.yml`: ```yaml name: Build & Test on: [push, pull_request] jobs: build-and-test: runs-on: ubuntu-latest container: vde-builder:latest steps: - name: Build run: | cmake -B build_ci -DCMAKE_BUILD_TYPE=Release cmake --build build_ci -j2 - name: Test run: | cd build_ci && ctest --output-on-failure -j2 ``` ### CI 检查项 - [ ] 编译通过 (零错误零警告) - [ ] 所有测试通过 (ctest 返回 0) - [ ] ASan 检查无内存错误 - [ ] 无新增编译警告