#include #include #include #include #include #include #include "core/algo/GprVolumeBuilder.hpp" #include "io/gpr/NormalizedRadarVolumeBridge.hpp" #ifdef _WIN32 #include #endif namespace fs = std::filesystem; namespace { // 在指定目录写出一组最小可解析的规范化 .head/.data(K=4 道 M=2 通道 N=3 采样)。 void writeMinimalLine(const fs::path& head, const fs::path& data) { { std::ofstream f(head); f << "SAMPLES:3\nNUMBER_OF_CH:2\nLAST_TRACE:8\nBITS:16\nENDIAN_TYPE:1\n" "DISTANCE_INTERVAL:0.1\nTIMEWINDOW:30\nDIELECTRIC:9\n"; } { std::ofstream f(data, std::ios::binary); for (int t = 0; t < 4; ++t) for (int c = 0; c < 2; ++c) for (int s = 0; s < 3; ++s) { std::int16_t v = static_cast(t * 10 + c * 100 + s); f.write(reinterpret_cast(&v), 2); } } } } // namespace TEST(NormalizedRadarBridge, BuildsVolumeWithExpectedAxes) { // K=4 道, M=2 通道, N=3 采样, 无通道偏移(不插值), coarse=1。 fs::path dir = fs::temp_directory_path() / "radar_bridge_test"; fs::create_directories(dir); { std::ofstream f(dir / "L.head"); f << "SAMPLES:3\nNUMBER_OF_CH:2\nLAST_TRACE:8\nBITS:16\nENDIAN_TYPE:1\n" "DISTANCE_INTERVAL:0.1\nTIMEWINDOW:30\nDIELECTRIC:9\n"; } { std::ofstream f(dir / "L.data", std::ios::binary); for (int t = 0; t < 4; ++t) for (int c = 0; c < 2; ++c) for (int s = 0; s < 3; ++s) { std::int16_t v = static_cast(t * 10 + c * 100 + s); f.write(reinterpret_cast(&v), 2); } } const auto b = geopro::io::gpr::buildLineVolumeFromNormalized( (dir).string(), "L", /*coarse=*/1, /*targetDy=*/0.0); // targetDy=0 不插值 EXPECT_EQ(b.vol.nx(), 4); // 道 EXPECT_EQ(b.vol.ny(), 2); // 通道 EXPECT_EQ(b.vol.nz(), 3); // 采样 EXPECT_DOUBLE_EQ(b.spacing[0], 0.1); // dx=DISTANCE_INTERVAL EXPECT_GT(b.spacing[2], 0.0); // dz 由 timewindow/dielectric 求得 >0 EXPECT_NEAR(b.quant.toPhys(b.vol.at(3, 1, 2)), 132.0, b.quant.scale); // t3c1s2=30+100+2 } // 回归:中文目录路径必须能打开渲染。app 传入的是 QString::toLocal8Bit(),即当前 // ANSI 代码页(简中=GBK)窄字节。关键复现条件——GUI app 链接 QWebEngine(Chromium)/VTK, // 它们在启动时 setlocale(LC_ALL,"") 把 LC_CTYPE 提升为系统 UTF-8 locale;此后窄字符 // ifstream 会把 GBK 路径字节当 UTF-8 解析 → open 失败(即"打开 .head 失败")。 // 故本测试显式置 UTF-8 locale 复现该失败面,走宽字符打开(见 io/gpr/LocalPath)守护回归。 // (纯 "C" locale 下 UCRT 用 CP_ACP=GBK 解窄路径,反而不失败,无法复现——须置 UTF-8。) TEST(NormalizedRadarBridge, OpensCjkDirectoryPathUnderUtf8Locale) { #ifdef _WIN32 const std::wstring wname = L"radar_cjk_南同大道"; const fs::path dir = fs::temp_directory_path() / wname; fs::remove_all(dir); fs::create_directories(dir); writeMinimalLine(dir / L"南同大道_000.head", dir / L"南同大道_000.data"); // 模拟 app:宽字符 → 当前 ANSI 代码页(GBK)窄字节,等价 QString::toLocal8Bit()。 auto toAcp = [](const std::wstring& w) { const int n = ::WideCharToMultiByte(CP_ACP, 0, w.data(), static_cast(w.size()), nullptr, 0, nullptr, nullptr); std::string s(static_cast(n), '\0'); ::WideCharToMultiByte(CP_ACP, 0, w.data(), static_cast(w.size()), s.data(), n, nullptr, nullptr); return s; }; const std::string dirAcp = toAcp(dir.wstring()); const std::string prefixAcp = toAcp(L"南同大道_000"); // 复现 app 运行期的 UTF-8 C locale(QWebEngine/VTK 所置)——不修复则 narrow open 失败。 const char* prevC = std::setlocale(LC_ALL, nullptr); const std::string savedC = prevC ? prevC : "C"; std::setlocale(LC_ALL, ".UTF-8"); geopro::core::BuiltI16 b; try { b = geopro::io::gpr::buildLineVolumeFromNormalized( dirAcp, prefixAcp, /*coarse=*/1, /*targetDy=*/0.0); } catch (...) { std::setlocale(LC_ALL, savedC.c_str()); fs::remove_all(dir); throw; // 未修复时会在此抛"打开 .head 失败"→ 测试红,正是回归守护 } std::setlocale(LC_ALL, savedC.c_str()); EXPECT_EQ(b.vol.nx(), 4); EXPECT_EQ(b.vol.ny(), 2); EXPECT_EQ(b.vol.nz(), 3); fs::remove_all(dir); #else GTEST_SKIP() << "中文窄路径打开问题仅 Windows 相关"; #endif }