Files
ViewDesignEngine/tests/fuzz/fuzz_format.cpp
T
茂之钳 7b76689ea1
CI / Build & Test (push) Failing after 41s
CI / Release Build (push) Failing after 30s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v11): CI/CD + regression tests + fuzzing + benchmarks + code quality
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)
2026-07-27 01:10:58 +08:00

300 lines
9.9 KiB
C++
Raw Blame History

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.
/// @file fuzz_format.cpp
/// @brief 格式往返模糊测试(IGES/STEP)+ 持续运行模式
///
/// 测试策略:
/// 1. 随机生成 B-Rep 体 → IGES 导出 → IGES 导入 → 验证一致性
/// 2. 随机生成 B-Rep 体 → STEP 导出 → STEP 导入 → 验证一致性
/// 3. 极端尺寸的格式往返
/// 4. 多体格式往返
/// 5. 持续运行模式
#include <gtest/gtest.h>
#include "vde/brep/brep.h"
#include "vde/brep/modeling.h"
#include "vde/brep/iges_export.h"
#include "vde/brep/iges_import.h"
#include "vde/brep/step_export.h"
#include "vde/brep/step_import.h"
#include "vde/core/point.h"
#include <cmath>
#include <random>
#include <string>
#include <cstdlib>
using namespace vde::brep;
using namespace vde::core;
static std::mt19937 s_rng(42);
namespace {
double rand_range(double lo, double hi) {
return std::uniform_real_distribution<double>(lo, hi)(s_rng);
}
BrepModel rand_body() {
int choice = static_cast<int>(rand_range(0, 3));
switch (choice) {
case 0:
return make_box(rand_range(0.5, 10.0),
rand_range(0.5, 10.0),
rand_range(0.5, 10.0));
case 1:
return make_cylinder(rand_range(0.3, 5.0),
rand_range(0.5, 10.0),
static_cast<int>(rand_range(8, 32)));
default:
return make_sphere(rand_range(0.3, 5.0),
static_cast<int>(rand_range(8, 24)),
static_cast<int>(rand_range(4, 12)));
}
}
} // anonymous
// ═══════════════════════════════════════════════
// IGES 随机往返
// ═══════════════════════════════════════════════
TEST(FuzzFormat, Iges_RandomRoundTrip) {
for (int i = 0; i < 30; ++i) {
auto body = rand_body();
EXPECT_TRUE(body.is_valid());
// 导出 IGES
std::string iges;
EXPECT_NO_THROW({
iges = export_iges({body});
});
EXPECT_FALSE(iges.empty()) << "IGES export failed at iteration " << i;
// 重新导入
std::vector<BrepModel> loaded;
EXPECT_NO_THROW({
loaded = import_iges_from_string(iges);
});
EXPECT_FALSE(loaded.empty()) << "IGES import failed at iteration " << i;
// 验证导入体
for (auto& lb : loaded) {
EXPECT_TRUE(lb.is_valid()) << "imported body invalid at iteration " << i;
}
}
}
TEST(FuzzFormat, Iges_MultiBodyRoundTrip) {
for (int i = 0; i < 15; ++i) {
int count = static_cast<int>(rand_range(2, 5));
std::vector<BrepModel> bodies;
for (int j = 0; j < count; ++j) {
bodies.push_back(rand_body());
}
std::string iges;
EXPECT_NO_THROW({ iges = export_iges(bodies); });
EXPECT_FALSE(iges.empty());
std::vector<BrepModel> loaded;
EXPECT_NO_THROW({ loaded = import_iges_from_string(iges); });
EXPECT_FALSE(loaded.empty());
EXPECT_GE(loaded.size(), 1u); // 可能合并或保留多个体
for (auto& lb : loaded) {
EXPECT_TRUE(lb.is_valid());
}
}
}
TEST(FuzzFormat, Iges_ExtremeSizes) {
// 极端尺寸的 IGES 往返
double sizes[] = {1e-3, 1e-1, 1.0, 10.0, 100.0, 1000.0};
for (double s : sizes) {
auto body = make_box(s, s * 0.5, s * 2.0);
EXPECT_NO_THROW({
std::string iges = export_iges({body});
EXPECT_FALSE(iges.empty());
auto loaded = import_iges_from_string(iges);
EXPECT_FALSE(loaded.empty());
EXPECT_TRUE(loaded[0].is_valid());
}) << "failed at size=" << s;
}
}
// ═══════════════════════════════════════════════
// STEP 随机往返
// ═══════════════════════════════════════════════
TEST(FuzzFormat, Step_RandomRoundTrip) {
for (int i = 0; i < 30; ++i) {
auto body = rand_body();
EXPECT_TRUE(body.is_valid());
std::string step;
EXPECT_NO_THROW({
step = export_step({body});
});
EXPECT_FALSE(step.empty()) << "STEP export failed at iteration " << i;
std::vector<BrepModel> loaded;
EXPECT_NO_THROW({
loaded = import_step_from_string(step);
});
EXPECT_FALSE(loaded.empty()) << "STEP import failed at iteration " << i;
for (auto& lb : loaded) {
EXPECT_TRUE(lb.is_valid()) << "imported body invalid at iteration " << i;
}
}
}
TEST(FuzzFormat, Step_MultiBodyRoundTrip) {
for (int i = 0; i < 15; ++i) {
int count = static_cast<int>(rand_range(2, 5));
std::vector<BrepModel> bodies;
for (int j = 0; j < count; ++j) {
bodies.push_back(rand_body());
}
std::string step;
EXPECT_NO_THROW({ step = export_step(bodies); });
EXPECT_FALSE(step.empty());
std::vector<BrepModel> loaded;
EXPECT_NO_THROW({ loaded = import_step_from_string(step); });
EXPECT_FALSE(loaded.empty());
for (auto& lb : loaded) {
EXPECT_TRUE(lb.is_valid());
}
}
}
TEST(FuzzFormat, Step_ExtremeSizes) {
double sizes[] = {1e-3, 1e-1, 1.0, 10.0, 100.0, 1000.0};
for (double s : sizes) {
auto body = make_box(s, s * 0.5, s * 2.0);
EXPECT_NO_THROW({
std::string step = export_step({body});
EXPECT_FALSE(step.empty());
auto loaded = import_step_from_string(step);
EXPECT_FALSE(loaded.empty());
EXPECT_TRUE(loaded[0].is_valid());
}) << "failed at size=" << s;
}
}
// ═══════════════════════════════════════════════
// 交叉格式往返(IGES → STEP → IGES
// ═══════════════════════════════════════════════
TEST(FuzzFormat, CrossFormat_IGES_to_STEP) {
for (int i = 0; i < 15; ++i) {
auto body = rand_body();
EXPECT_NO_THROW({
// IGES export → STEP import
std::string iges = export_iges({body});
auto from_iges = import_iges_from_string(iges);
EXPECT_FALSE(from_iges.empty());
// STEP export → IGES import
std::string step = export_step({body});
auto from_step = import_step_from_string(step);
EXPECT_FALSE(from_step.empty());
// Both should be valid
EXPECT_TRUE(from_iges[0].is_valid());
EXPECT_TRUE(from_step[0].is_valid());
});
}
}
TEST(FuzzFormat, CrossFormat_STEP_to_IGES) {
for (int i = 0; i < 15; ++i) {
auto body = rand_body();
EXPECT_NO_THROW({
// STEP → IGES
std::string step = export_step({body});
auto from_step = import_step_from_string(step);
ASSERT_FALSE(from_step.empty());
std::string iges = export_iges(from_step);
auto from_iges = import_iges_from_string(iges);
EXPECT_FALSE(from_iges.empty());
EXPECT_TRUE(from_iges[0].is_valid());
});
}
}
// ═══════════════════════════════════════════════
// 布尔运算后的格式往返
// ═══════════════════════════════════════════════
TEST(FuzzFormat, BooleanThenRoundTrip) {
for (int i = 0; i < 10; ++i) {
auto a = rand_body();
auto b = rand_body();
EXPECT_NO_THROW({
// 先做布尔运算
auto u = brep_union(a, b);
// IGES 往返
std::string iges = export_iges({u});
auto from_iges = import_iges_from_string(iges);
EXPECT_FALSE(from_iges.empty());
EXPECT_TRUE(from_iges[0].is_valid());
// STEP 往返
std::string step = export_step({u});
auto from_step = import_step_from_string(step);
EXPECT_FALSE(from_step.empty());
EXPECT_TRUE(from_step[0].is_valid());
});
}
}
// ═══════════════════════════════════════════════
// 持续运行模式
// ═══════════════════════════════════════════════
TEST(FuzzFormat, ContinuousFuzz) {
const char* forever = std::getenv("VDE_FUZZ_FOREVER");
if (!forever || std::string(forever) != "1") {
GTEST_SKIP() << "设置 VDE_FUZZ_FOREVER=1 启用持续模糊测试";
}
int iteration = 0;
while (true) {
++iteration;
auto body = rand_body();
ASSERT_TRUE(body.is_valid());
// IGES 往返
std::string iges = export_iges({body});
ASSERT_FALSE(iges.empty());
auto from_iges = import_iges_from_string(iges);
ASSERT_FALSE(from_iges.empty()) << "IGES import failed at iteration " << iteration;
ASSERT_TRUE(from_iges[0].is_valid());
// STEP 往返
std::string step = export_step({body});
ASSERT_FALSE(step.empty());
auto from_step = import_step_from_string(step);
ASSERT_FALSE(from_step.empty()) << "STEP import failed at iteration " << iteration;
ASSERT_TRUE(from_step[0].is_valid());
if (iteration % 1000 == 0) {
std::cout << "[FuzzFormat] iteration=" << iteration
<< " iges_len=" << iges.size()
<< " step_len=" << step.size()
<< std::endl;
}
}
}