feat(v11.3): binary format support — Parasolid XT binary + ACIS SAB + JT binary
- industrial_formats.h: +46 lines (XtBinaryHeader, SabHeader, JtSegmentHeader structs + binary API) - industrial_formats.cpp: +310 lines (XT binary encode/decode, SAB parse, JT binary stream, auto-detect) - test_industrial_formats.cpp: +187 lines (12 binary tests) - 44/44 tests passing, zero regressions Auto-detect: magic bytes check before text parsing for all formats
This commit is contained in:
@@ -195,9 +195,196 @@ TEST(IndustrialParasolid, ImportXtNonexistentFile) {
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// ACIS SAT 测试
|
||||
// Parasolid XT 二进制测试
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
TEST(IndustrialParasolidBinary, ExportBoxToXBinary) {
|
||||
auto box = make_test_box();
|
||||
const char* path = "/tmp/test_box.x_b";
|
||||
clean(path);
|
||||
|
||||
ASSERT_NO_THROW(export_parasolid_xt_binary(box, path));
|
||||
|
||||
// 检查文件存在且以 "PARA" 开头
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
ASSERT_TRUE(f.good());
|
||||
char magic[4] = {};
|
||||
f.read(magic, 4);
|
||||
EXPECT_EQ(magic[0], 'P');
|
||||
EXPECT_EQ(magic[1], 'A');
|
||||
EXPECT_EQ(magic[2], 'R');
|
||||
EXPECT_EQ(magic[3], 'A');
|
||||
|
||||
clean(path);
|
||||
}
|
||||
|
||||
TEST(IndustrialParasolidBinary, ImportExportedXBinary) {
|
||||
auto box = make_test_box();
|
||||
const char* path = "/tmp/test_xb_roundtrip.x_b";
|
||||
clean(path);
|
||||
|
||||
export_parasolid_xt_binary(box, path);
|
||||
auto imported = import_parasolid_xt(path); // auto-detect binary
|
||||
expect_valid_body(imported);
|
||||
|
||||
clean(path);
|
||||
}
|
||||
|
||||
TEST(IndustrialParasolidBinary, ExportBinaryToString) {
|
||||
auto box = make_test_box();
|
||||
std::string bin = export_parasolid_xt_binary_str(box);
|
||||
EXPECT_GT(bin.size(), 40u) << "Binary data should exceed header size";
|
||||
// 检查 magic
|
||||
EXPECT_EQ(bin[0], 'P');
|
||||
EXPECT_EQ(bin[1], 'A');
|
||||
EXPECT_EQ(bin[2], 'R');
|
||||
EXPECT_EQ(bin[3], 'A');
|
||||
}
|
||||
|
||||
TEST(IndustrialParasolidBinary, ImportFromMemory) {
|
||||
auto box = make_test_box();
|
||||
std::string bin = export_parasolid_xt_binary_str(box);
|
||||
|
||||
auto imported = import_parasolid_xt_binary(
|
||||
reinterpret_cast<const uint8_t*>(bin.data()), bin.size());
|
||||
expect_valid_body(imported);
|
||||
}
|
||||
|
||||
TEST(IndustrialParasolidBinary, AutoDetectBinaryPath) {
|
||||
auto box = make_test_box();
|
||||
const char* path = "/tmp/test_autodetect.x_b";
|
||||
clean(path);
|
||||
export_parasolid_xt_binary(box, path);
|
||||
|
||||
// import_parasolid_xt should auto-detect binary format
|
||||
auto imported = import_parasolid_xt(path);
|
||||
expect_valid_body(imported);
|
||||
|
||||
clean(path);
|
||||
}
|
||||
|
||||
TEST(IndustrialParasolidBinary, ExportCylinderBinary) {
|
||||
auto cyl = make_test_cylinder();
|
||||
const char* path = "/tmp/test_cyl.x_b";
|
||||
clean(path);
|
||||
|
||||
export_parasolid_xt_binary(cyl, path);
|
||||
auto imported = import_parasolid_xt(path);
|
||||
expect_valid_body(imported);
|
||||
|
||||
clean(path);
|
||||
}
|
||||
|
||||
TEST(IndustrialParasolidBinary, InvalidMagicThrows) {
|
||||
// 制造无效的二进制数据
|
||||
uint8_t bad_data[40] = {};
|
||||
bad_data[0] = 'X'; bad_data[1] = 'X'; bad_data[2] = 'X'; bad_data[3] = 'X';
|
||||
EXPECT_THROW(import_parasolid_xt_binary(bad_data, 40), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(IndustrialParasolidBinary, TooSmallThrows) {
|
||||
uint8_t tiny[4] = {'P', 'A', 'R', 'A'};
|
||||
EXPECT_THROW(import_parasolid_xt_binary(tiny, 4), std::runtime_error);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// ACIS SAB 二进制测试
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
TEST(IndustrialACISSAB, ImportMinimalSAB) {
|
||||
// 构造最小 SAB 数据: header(16) + 空字符串表 + 0浮点 + 0整数
|
||||
std::string sab;
|
||||
sab += "SAB "; // magic
|
||||
uint32_t data_size = 16; // header only
|
||||
sab.push_back(static_cast<char>(data_size & 0xFF));
|
||||
sab.push_back(static_cast<char>((data_size >> 8) & 0xFF));
|
||||
sab.push_back(static_cast<char>((data_size >> 16) & 0xFF));
|
||||
sab.push_back(static_cast<char>((data_size >> 24) & 0xFF));
|
||||
uint32_t version = 2700; // V27
|
||||
sab.push_back(static_cast<char>(version & 0xFF));
|
||||
sab.push_back(static_cast<char>((version >> 8) & 0xFF));
|
||||
sab.push_back(static_cast<char>((version >> 16) & 0xFF));
|
||||
sab.push_back(static_cast<char>((version >> 24) & 0xFF));
|
||||
uint32_t str_size = 0;
|
||||
sab.push_back(static_cast<char>(str_size & 0xFF));
|
||||
sab.push_back(static_cast<char>((str_size >> 8) & 0xFF));
|
||||
sab.push_back(static_cast<char>((str_size >> 16) & 0xFF));
|
||||
sab.push_back(static_cast<char>((str_size >> 24) & 0xFF));
|
||||
|
||||
// 不应崩溃
|
||||
auto model = import_acis_sat_binary(
|
||||
reinterpret_cast<const uint8_t*>(sab.data()), sab.size());
|
||||
// 空数据可能返回空模型
|
||||
EXPECT_NO_THROW(import_acis_sat_binary(
|
||||
reinterpret_cast<const uint8_t*>(sab.data()), sab.size()));
|
||||
}
|
||||
|
||||
TEST(IndustrialACISSAB, SATFileAutoDetectSAB) {
|
||||
// 写入 SAB 格式文件,import_acis_sat 应自动检测
|
||||
std::string sab;
|
||||
sab += "SAB ";
|
||||
for (int i = 0; i < 12; ++i) sab.push_back('\0'); // 填充 header
|
||||
const char* path = "/tmp/test_autodetect.sab";
|
||||
clean(path);
|
||||
{
|
||||
std::ofstream f(path, std::ios::binary);
|
||||
f.write(sab.data(), static_cast<std::streamsize>(sab.size()));
|
||||
}
|
||||
|
||||
auto model = import_acis_sat(path);
|
||||
// 应能检测 SAB 格式而不崩溃
|
||||
EXPECT_NO_THROW(import_acis_sat(path));
|
||||
|
||||
clean(path);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// JT 二进制测试
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
TEST(IndustrialJTBinary, ImportFromMemory) {
|
||||
auto box = make_test_box();
|
||||
const char* path = "/tmp/test_jt_bin.jt";
|
||||
clean(path);
|
||||
export_jt(box, path);
|
||||
|
||||
// 读取文件内容作为二进制数据
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
std::string data((std::istreambuf_iterator<char>(f)), {});
|
||||
f.close();
|
||||
|
||||
JTOptions opts;
|
||||
opts.load_mesh = true;
|
||||
opts.load_brep = false;
|
||||
|
||||
auto imported = import_jt_binary(
|
||||
reinterpret_cast<const uint8_t*>(data.data()), data.size(), opts);
|
||||
expect_valid_body(imported);
|
||||
|
||||
clean(path);
|
||||
}
|
||||
|
||||
TEST(IndustrialJTBinary, ImportFromMemoryWithBrepOption) {
|
||||
auto box = make_test_box();
|
||||
const char* path = "/tmp/test_jt_bin2.jt";
|
||||
clean(path);
|
||||
export_jt(box, path);
|
||||
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
std::string data((std::istreambuf_iterator<char>(f)), {});
|
||||
f.close();
|
||||
|
||||
JTOptions opts;
|
||||
opts.load_brep = true;
|
||||
opts.load_mesh = false;
|
||||
|
||||
// 不应崩溃
|
||||
EXPECT_NO_THROW(import_jt_binary(
|
||||
reinterpret_cast<const uint8_t*>(data.data()), data.size(), opts));
|
||||
|
||||
clean(path);
|
||||
}
|
||||
|
||||
TEST(IndustrialACIS, ExportBoxToSatV27) {
|
||||
auto box = make_test_box();
|
||||
const char* path = "/tmp/test_box.sat";
|
||||
|
||||
Reference in New Issue
Block a user