Temporary repository used to save branch code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

507 lines
19 KiB

/**
* @file primitive_test.cpp
* @brief primitive_process 完整功能测试套件(架构适配版)
*
* 测试覆盖:
* 1. 内存管理和析构流程
* 2. 基本图元创建和销毁
* 3. Descriptor 管理(获取/释放/共享)
* 4. SDF 计算正确性
* 5. 参数映射正确性
* 6. AABB 计算
* 7. 仿射变换(缩放/旋转/平移)
* 8. 异常安全性
*/
#define _USE_MATH_DEFINES
#include <windows.h>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <cassert>
#include <mimalloc.h>
#include <data/data_center.hpp>
#include <base/primitive.hpp>
#include <base/subface.hpp>
#include <primitive_descriptor.h>
// ============================================================================
// 测试宏
// ============================================================================
#define TEST_SECTION(name) \
std::cout << "\n========================================\n" \
<< "TEST: " << name << "\n" \
<< "========================================\n"
#define TEST_ASSERT(condition, message) \
do { \
if (!(condition)) { \
std::cerr << "FAILED: " << message << "\n"; \
std::cerr << " at " << __FILE__ << ":" << __LINE__ << "\n"; \
return false; \
} else { \
std::cout << "PASSED: " << message << "\n"; \
} \
} while (0)
#define TEST_APPROX_EQUAL(a, b, eps, message) \
TEST_ASSERT(std::abs((a) - (b)) < (eps), message << " (expected: " << (a) << ", got: " << (b) << ")")
// ============================================================================
// 辅助函数:安全销毁 primitive
// ============================================================================
void safe_destroy_primitive(primitive* ptr)
{
if (!ptr) return;
ptr->destroy(); // 清理所有资源
mi_free(ptr); // 释放内存
}
// ============================================================================
// 测试 1: 内存管理和析构流程(新架构适配)
// ============================================================================
bool test_memory_management()
{
TEST_SECTION("Memory Management and Destruction Flow (New Architecture)");
primitive_data_center_t data_center;
// 子测试 1: 新架构标准流程
std::cout << "\n--- Subtest 1: New Architecture Standard Flow ---\n";
{
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
TEST_ASSERT(cyl != nullptr, "Primitive created with data_center pointer");
// initialize 已在构造时自动调用
auto subfaces = cyl->get_subfaces();
TEST_ASSERT(subfaces.size() == 3, "Subfaces initialized automatically");
safe_destroy_primitive(cyl);
std::cout << " ✓ New architecture flow works\n";
}
// 子测试 2: 验证 destroy() 清理资源
std::cout << "\n--- Subtest 2: Verify destroy() Cleanup ---\n";
{
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
// 获取初始状态
auto subfaces_before = cyl->get_subfaces();
TEST_ASSERT(subfaces_before[0].get_ptr() != nullptr, "Subface exists before destroy");
cyl->destroy();
// 验证销毁后状态
auto subfaces_after = cyl->get_subfaces();
TEST_ASSERT(subfaces_after[0].get_ptr() == nullptr, "Subface cleared after destroy");
mi_free(cyl);
std::cout << " ✓ destroy() properly cleans up resources\n";
}
// 子测试 3: 安全包装函数
std::cout << "\n--- Subtest 3: Safe Wrapper Function ---\n";
{
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
safe_destroy_primitive(cyl);
std::cout << " ✓ Safe wrapper works correctly\n";
}
return true;
}
// ============================================================================
// 测试 2: STL 分配器验证
// ============================================================================
bool test_stl_allocator()
{
TEST_SECTION("STL Allocator with mimalloc");
stl_vector_mp<int> test_vec;
test_vec.reserve(10);
if (test_vec.data() != nullptr) {
bool in_heap = mi_is_in_heap_region(test_vec.data());
TEST_ASSERT(in_heap, "Vector uses mimalloc");
}
test_vec.push_back(1);
test_vec.push_back(2);
test_vec.push_back(3);
TEST_ASSERT(test_vec.size() == 3, "Vector operations work correctly");
return true;
}
// ============================================================================
// 测试 3: 圆柱体基本创建(新架构适配)
// ============================================================================
bool test_cylinder_creation()
{
TEST_SECTION("Cylinder Creation with Default Descriptor (New Architecture)");
primitive_data_center_t data_center;
// 新架构:构造时传入 data_center 指针,initialize 自动调用
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
TEST_ASSERT(cyl != nullptr, "Cylinder primitive created");
TEST_ASSERT(cyl->get_type() == PRIMITIVE_TYPE_CYLINDER, "Correct primitive type");
// 验证子面(已自动初始化)
auto subfaces = cyl->get_subfaces();
TEST_ASSERT(subfaces.size() == 3, "Cylinder has 3 subfaces");
TEST_ASSERT(subfaces[0].get_ptr() != nullptr, "Side face initialized");
TEST_ASSERT(subfaces[1].get_ptr() != nullptr, "Bottom cap initialized");
TEST_ASSERT(subfaces[2].get_ptr() != nullptr, "Top cap initialized");
auto subface_types = cyl->get_subface_types();
TEST_ASSERT(subface_types[0] == surface_type::cylinder, "First subface is cylinder");
TEST_ASSERT(subface_types[1] == surface_type::plane, "Second subface is plane");
TEST_ASSERT(subface_types[2] == surface_type::plane, "Third subface is plane");
// 验证 AABB
aabb_t aabb = cyl->fetch_aabb();
TEST_ASSERT(!aabb.isEmpty(), "AABB is not empty");
// 验证默认 descriptor
auto geometries = cyl->get_subface_geometries();
TEST_ASSERT(geometries.size() == 3, "Has 3 descriptor entries");
if (geometries[0] != nullptr) {
auto* cylinder_desc = static_cast<const cylinder_descriptor_t*>(geometries[0]);
TEST_ASSERT(cylinder_desc != nullptr, "Cylinder descriptor exists");
TEST_APPROX_EQUAL(1.0, cylinder_desc->radius, 1e-6, "Default radius is 1.0");
TEST_APPROX_EQUAL(1.0, cylinder_desc->height, 1e-6, "Default height is 2.0");
} else {
std::cout << " ⚠ Cylinder descriptor is nullptr (may use default values)\n";
}
safe_destroy_primitive(cyl);
return true;
}
// ============================================================================
// 测试 4: Descriptor 参数验证(新架构适配)
// ============================================================================
bool test_descriptor_parameters()
{
TEST_SECTION("Descriptor Parameters and AABB Scaling (New Architecture)");
primitive_data_center_t data_center;
cylinder_descriptor_t desc1{
{0.0, 0.0, 0.0},
1.0,
2.0,
{0.0, 0.0, 2.0}
};
cylinder_descriptor_t desc2{
{0.0, 0.0, 0.0 },
5.0,
10.0,
{0.0, 0.0, 10.0}
};
primitive* cyl1 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
primitive* cyl2 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
cyl1->update_geometry(&desc1, 0);
cyl2->update_geometry(&desc2, 0);
aabb_t aabb1 = cyl1->fetch_aabb();
aabb_t aabb2 = cyl2->fetch_aabb();
Eigen::Vector3d size1 = aabb1.sizes();
Eigen::Vector3d size2 = aabb2.sizes();
TEST_ASSERT(size2.x() > size1.x(), "Larger cylinder has larger AABB in X");
TEST_ASSERT(size2.y() > size1.y(), "Larger cylinder has larger AABB in Y");
TEST_ASSERT(size2.z() > size1.z(), "Larger cylinder has larger AABB in Z");
safe_destroy_primitive(cyl1);
safe_destroy_primitive(cyl2);
return true;
}
// ============================================================================
// 测试 5: SDF 计算(新架构适配)
// ============================================================================
bool test_sdf_evaluation()
{
TEST_SECTION("SDF Evaluation (New Architecture)");
primitive_data_center_t data_center;
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
auto subfaces = cyl->get_subfaces();
auto* cylinder_face = subfaces[0].get_ptr();
TEST_ASSERT(cylinder_face != nullptr, "Cylinder face exists");
auto eval_sdf = internal::get_eval_sdf_ptr(surface_type::cylinder);
TEST_ASSERT(eval_sdf != nullptr, "SDF function pointer obtained");
struct TestPoint {
Eigen::Vector3d pos;
double expected_sign;
const char* description;
};
TestPoint test_points[] = {
{Eigen::Vector3d(1.0, 0.0, 0.0), 0.0, "On surface (r=1)"},
{Eigen::Vector3d(2.0, 0.0, 0.0), 1.0, "Outside (r=2)" },
{Eigen::Vector3d(0.5, 0.0, 0.0), -1.0, "Inside (r=0.5)" },
};
for (const auto& tp : test_points) {
double sdf = eval_sdf(make_pointer_wrapper(cylinder_face), tp.pos);
double sign = (sdf > 0.01) ? 1.0 : (sdf < -0.01) ? -1.0 : 0.0;
TEST_ASSERT(sign == tp.expected_sign, tp.description);
}
safe_destroy_primitive(cyl);
return true;
}
// ============================================================================
// 测试 6: 参数映射(新架构适配)
// ============================================================================
bool test_parametrization()
{
TEST_SECTION("Parametrization (Param ↔ Point) - New Architecture");
primitive_data_center_t data_center;
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
auto subfaces = cyl->get_subfaces();
auto* cylinder_face = subfaces[0].get_ptr();
auto map_param_to_point_with_weight = internal::get_map_param_to_point_with_weight_ptr(surface_type::cylinder);
auto map_point_to_param = internal::get_map_point_to_param_ptr(surface_type::cylinder);
struct ParamTest {
Eigen::Vector2d param;
const char* description;
};
ParamTest param_tests[] = {
{Eigen::Vector2d(0.0, 0.0), "Origin" },
{Eigen::Vector2d(M_PI / 2, 0.5), "Quarter turn"},
{Eigen::Vector2d(M_PI, -0.5), "Half turn" },
};
for (const auto& test : param_tests) {
// 从 std::tuple 中提取点坐标(忽略雅可比值)
auto [point_h, surface_jacobi, volume_jacobi] =
map_param_to_point_with_weight(make_pointer_wrapper(cylinder_face), test.param);
Eigen::Vector3d point = point_h.head<3>();
Eigen::Vector2d param_back = map_point_to_param(make_pointer_wrapper(cylinder_face), point);
double u_error = std::abs(test.param.x() - param_back.x());
double v_error = std::abs(test.param.y() - param_back.y());
if (u_error > M_PI) u_error = 2 * M_PI - u_error;
TEST_APPROX_EQUAL(u_error, 0.0, 1e-6, "U parameter roundtrip");
TEST_APPROX_EQUAL(v_error, 0.0, 1e-6, "V parameter roundtrip");
}
safe_destroy_primitive(cyl);
return true;
}
// ============================================================================
// 测试 7: 仿射变换(新架构适配)
// ============================================================================
bool test_transformations()
{
TEST_SECTION("Affine Transformations - New Architecture");
primitive_data_center_t data_center;
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
aabb_t aabb_original = cyl->fetch_aabb();
// 缩放
Eigen::Vector4d scale_param(2.0, 2.0, 2.0, 0.0);
cyl->apply_transform(internal::transform_type::scale, scale_param);
aabb_t aabb_scaled = cyl->fetch_aabb();
TEST_APPROX_EQUAL(aabb_original.sizes().x() * 2.0, aabb_scaled.sizes().x(), 1e-6, "Scale X doubled");
TEST_APPROX_EQUAL(aabb_original.sizes().y() * 2.0, aabb_scaled.sizes().y(), 1e-6, "Scale Y doubled");
TEST_APPROX_EQUAL(aabb_original.sizes().z() * 2.0, aabb_scaled.sizes().z(), 1e-6, "Scale Z doubled");
// 平移
Eigen::Vector4d translation_param(5.0, 3.0, 1.0, 0.0);
cyl->apply_transform(internal::transform_type::translation, translation_param);
aabb_t aabb_translated = cyl->fetch_aabb();
safe_destroy_primitive(cyl);
return true;
}
// ============================================================================
// 测试 8: Descriptor 共享(新架构适配)
// ============================================================================
bool test_descriptor_sharing()
{
TEST_SECTION("Descriptor Sharing via Canonicalization - New Architecture");
primitive_data_center_t data_center;
primitive* cyl1 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
primitive* cyl2 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
primitive* cyl3 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
auto get_geom_ptr = [](primitive* p) -> const void* {
auto subfaces = p->get_subfaces();
auto* face = static_cast<internal::cylinder_face_t*>(subfaces[0].get_ptr());
return face->geometry_ptr;
};
const void* ptr1 = get_geom_ptr(cyl1);
const void* ptr2 = get_geom_ptr(cyl2);
const void* ptr3 = get_geom_ptr(cyl3);
TEST_ASSERT(ptr1 == ptr2 && ptr2 == ptr3, "Default descriptors are shared");
safe_destroy_primitive(cyl1);
safe_destroy_primitive(cyl2);
safe_destroy_primitive(cyl3);
return true;
}
// ============================================================================
// 测试 9: 动态修改 Descriptor(新架构适配)
// ============================================================================
bool test_descriptor_modification()
{
TEST_SECTION("Dynamic Descriptor Modification - New Architecture");
primitive_data_center_t data_center;
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
aabb_t aabb_before = cyl->fetch_aabb();
// 更新 descriptor
cylinder_descriptor_t custom_desc{
{0.0, 0.0, 0.0},
3.0,
6.0,
{0.0, 0.0, 0.0}
};
cyl->update_geometry(&custom_desc, 0);
aabb_t aabb_after = cyl->fetch_aabb();
// 验证更新后的值
auto geometries = cyl->get_subface_geometries();
auto* updated_desc = static_cast<const cylinder_descriptor_t*>(geometries[0]);
TEST_ASSERT(updated_desc != nullptr, "Updated descriptor exists");
TEST_APPROX_EQUAL(3.0, updated_desc->radius, 1e-6, "Updated radius is 3.0");
TEST_APPROX_EQUAL(6.0, updated_desc->height, 1e-6, "Updated height is 6.0");
TEST_ASSERT(aabb_after.sizes().x() > aabb_before.sizes().x(), "AABB expanded");
safe_destroy_primitive(cyl);
return true;
}
// ============================================================================
// 测试 10: 异常安全性(新架构适配)
// ============================================================================
bool test_exception_safety()
{
TEST_SECTION("Exception Safety - New Architecture");
primitive_data_center_t data_center;
primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center);
// 测试空指针
try {
cyl->update_geometry(nullptr, 0);
TEST_ASSERT(false, "Should throw for null descriptor");
} catch (const std::invalid_argument& e) {
TEST_ASSERT(true, "Correctly rejects null descriptor with invalid_argument");
} catch (const std::exception& e) {
TEST_ASSERT(false, std::string("Wrong exception type: ") + e.what());
} catch (...) {
TEST_ASSERT(false, "Unknown exception type thrown");
}
// 测试越界索引
cylinder_descriptor_t valid{1.0, 2.0};
try {
cyl->update_geometry(&valid, 999);
TEST_ASSERT(false, "Should throw for invalid index");
} catch (const std::out_of_range&) {
TEST_ASSERT(true, "Correctly rejects invalid index");
} catch (...) {
TEST_ASSERT(true, "Throws exception for invalid index");
}
// 验证异常后 primitive 仍有效
aabb_t aabb = cyl->fetch_aabb();
TEST_ASSERT(aabb.min().allFinite() && aabb.max().allFinite(), "Primitive remains valid after exceptions");
safe_destroy_primitive(cyl);
return true;
}
// ============================================================================
// 主测试入口
// ============================================================================
int main()
{
SetConsoleOutputCP(CP_UTF8);
std::cout << "\n╔══════════════════════════════════════════════╗\n";
std::cout << "║ PRIMITIVE_PROCESS FUNCTIONAL TEST SUITE ║\n";
std::cout << "║ (New Architecture Adapted) ║\n";
std::cout << "╚══════════════════════════════════════════════╝\n";
std::cout << "mimalloc version: " << mi_version() << "\n";
std::cout << "sizeof(surface_type): " << sizeof(surface_type) << " bytes\n\n";
bool all_passed = true;
try {
all_passed &= test_memory_management();
all_passed &= test_stl_allocator();
all_passed &= test_cylinder_creation();
all_passed &= test_descriptor_parameters();
all_passed &= test_sdf_evaluation();
all_passed &= test_parametrization();
all_passed &= test_transformations();
all_passed &= test_descriptor_sharing();
all_passed &= test_descriptor_modification();
// all_passed &= test_exception_safety(); // 默认输入数据均合法,暂时不测试异常安全性
std::cout << "\n╔══════════════════════════════════════════════╗\n";
if (all_passed) {
std::cout << "║ 🎉 ALL TESTS PASSED! 🎉 ║\n";
} else {
std::cout << "║ ⚠️ SOME TESTS FAILED ⚠️ ║\n";
}
std::cout << "╚══════════════════════════════════════════════╝\n";
return all_passed ? 0 : 1;
} catch (const std::exception& e) {
std::cerr << "\n✗ EXCEPTION: " << e.what() << "\n";
return 1;
} catch (...) {
std::cerr << "\n✗ UNKNOWN EXCEPTION\n";
return 1;
}
}