From 8cdd6679a99c63006f9a5329bafa51ba9bc0bd8c Mon Sep 17 00:00:00 2001 From: gaozheng Date: Thu, 11 Jun 2026 20:31:07 +0800 Subject: [PATCH] =?UTF-8?q?harden(data):=20=E5=8F=A5=E6=9F=84=20emit=20don?= =?UTF-8?q?e=20=E7=A7=BB=E5=87=BA=20try=20+=20catch(...)=20=E5=85=9C?= =?UTF-8?q?=E5=BA=95=20+=20parse=20=E6=8A=9B=E5=BC=82=E5=B8=B8=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=EF=BC=88=E8=AF=84=E5=AE=A1=20I-1/M-5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data/api/DatasetLoadHandles.cpp | 20 ++++++++++++++++++-- tests/data/test_dataset_load_handles.cpp | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/data/api/DatasetLoadHandles.cpp b/src/data/api/DatasetLoadHandles.cpp index cd80aa8..ba23173 100644 --- a/src/data/api/DatasetLoadHandles.cpp +++ b/src/data/api/DatasetLoadHandles.cpp @@ -14,11 +14,19 @@ ApiChartLoad::ApiChartLoad(geopro::net::ApiBatch* batch, Parser parse, QObject* QObject::connect(batch, &geopro::net::ApiBatch::succeeded, this, [this](const QList& resps) { if (aborted_) return; // §5.0 + ChartParts parts; try { - emit done(parse_(resps)); + parts = parse_(resps); // 仅解析在 try 内:下游 done 处理器抛出不应误报为解析失败 } catch (const std::exception& e) { emit failed(QString::fromUtf8(e.what())); + deleteLater(); + return; + } catch (...) { // 非 std 异常跨信号槽会 terminate,兜底转 failed + emit failed(QStringLiteral("解析失败:未知异常")); + deleteLater(); + return; } + emit done(parts); deleteLater(); }); QObject::connect(batch, &geopro::net::ApiBatch::failed, this, @@ -41,11 +49,19 @@ ApiGridLoad::ApiGridLoad(geopro::net::ApiBatch* batch, Parser parse, QObject* pa QObject::connect(batch, &geopro::net::ApiBatch::succeeded, this, [this](const QList& resps) { if (aborted_) return; + GridParts parts; try { - emit done(parse_(resps)); + parts = parse_(resps); // 仅解析在 try 内:下游 done 处理器抛出不应误报为解析失败 } catch (const std::exception& e) { emit failed(QString::fromUtf8(e.what())); + deleteLater(); + return; + } catch (...) { + emit failed(QStringLiteral("解析失败:未知异常")); + deleteLater(); + return; } + emit done(parts); deleteLater(); }); QObject::connect(batch, &geopro::net::ApiBatch::failed, this, diff --git a/tests/data/test_dataset_load_handles.cpp b/tests/data/test_dataset_load_handles.cpp index 4dfd038..d0590c7 100644 --- a/tests/data/test_dataset_load_handles.cpp +++ b/tests/data/test_dataset_load_handles.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "api/DatasetLoadHandles.hpp" #include "net/FakeApiCall.hpp" @@ -40,6 +41,19 @@ TEST(DatasetLoadHandles, ChartLoadEmitsFailedOnBatchFailure) { EXPECT_EQ(failSpy.count(), 1); } +TEST(DatasetLoadHandles, ChartLoadEmitsFailedWhenParseThrows) { + auto* a = new FakeApiCall; + auto* batch = new ApiBatch({a}, isFailure); + auto* load = new ApiChartLoad(batch, [](const QList&) -> ChartParts { + throw std::runtime_error("parse boom"); + }); + QSignalSpy doneSpy(load, &ChartLoad::done); + QSignalSpy failSpy(load, &ChartLoad::failed); + a->fire(ok()); // batch 成功 → parse 抛异常 → failed(emit done 已移出 try) + EXPECT_EQ(doneSpy.count(), 0); + EXPECT_EQ(failSpy.count(), 1); +} + TEST(DatasetLoadHandles, GridLoadAbortSuppressesLateDone) { auto* a = new FakeApiCall; auto* batch = new ApiBatch({a}, isFailure);