1
测试指南
茂之钳 edited this page 2026-07-27 13:52:56 +08:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

测试指南

ViewDesignEngine 使用 Google Test 框架,通过 CTest 运行测试。

目录

  1. 测试架构
  2. 运行测试
  3. 编写测试
  4. 测试规范
  5. Sanitizer 测试
  6. 性能基准
  7. 模糊测试
  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. 运行测试

基础命令

# 构建并运行所有测试
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

直接运行可执行文件

# 运行单个测试文件
./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 中运行

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_<feature>.cpp

# 例如:为 brep 模块的新功能添加测试
touch tests/brep/test_new_feature.cpp

步骤 2: 编写测试代码

// tests/brep/test_new_feature.cpp
#include <gtest/gtest.h>
#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/<module>/CMakeLists.txt,使用标准宏:

# 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: 本地验证

# 配置构建
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 环境验证(推荐)

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 基本模板

// tests/mesh/test_my_feature.cpp
#include <gtest/gtest.h>
#include <vde/mesh/halfedge_mesh.h>
#include <vde/core/point.h>

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 参数化测试

// 使用不同参数重复测试
class BooleanOpTest : public ::testing::TestWithParam<
    std::tuple<std::string, int, int>> {};

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

# 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_<feature>.cpp
测试套件名: <Feature>Test 或 <Module>Test
测试用例名: <Action><Condition> 或 <MethodName><Scenario>

示例:
- 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)
  • 边界条件 (空输入、极值、零值)
  • 错误处理 (无效输入、超范围)
  • 退化情况 (共线/共面点、零面积面)
  • 数值稳定性 (接近容差的场景)
  • 性能回归 (大规模输入不超时)

反模式

// ❌ 测试依赖全局状态
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)

# 构建 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)

# 手动添加 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)

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. 性能基准

运行基准测试

# 构建基准测试
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. 模糊测试

# 构建并运行模糊测试
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:

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 检查无内存错误
  • 无新增编译警告