#include #include #include "DatasetDetailController.hpp" #include "IDatasetChartStrategy.hpp" #include "repo/IAsyncDatasetRepository.hpp" #include "api/DatasetLoadHandles.hpp" using namespace geopro; namespace { // 反演策略桩:散点 + 网格两阶段。 struct InversionStrategy : controller::IDatasetChartStrategy { std::string ddCode() const override { return "dd_inversion_data"; } bool hasGridPhase() const override { return true; } }; // 无网格阶段策略桩:仅散点(如纯散点类型)。 struct NoGridStrategy : controller::IDatasetChartStrategy { std::string ddCode() const override { return "dd_scatter_only"; } bool hasGridPhase() const override { return false; } }; // 注册了反演策略的注册表(多数用例复用)。 controller::ChartStrategyRegistry makeInversionRegistry() { controller::ChartStrategyRegistry reg; reg.add(std::make_unique()); return reg; } // 桩句柄:不声明 Q_OBJECT —— 发射继承自 data::ChartLoad/GridLoad 的信号、override abort。 struct StubChartLoad : data::ChartLoad { bool aborted = false; void abort() override { aborted = true; } void fireDone() { emit done(data::ChartParts{}); } void fireFailed() { emit failed(QStringLiteral("x")); } }; struct StubGridLoad : data::GridLoad { bool aborted = false; void abort() override { aborted = true; } void fireDone() { emit done(data::GridParts{}); } void fireFailed() { emit failed(QStringLiteral("x")); } }; struct StubAsyncRepo : data::IAsyncDatasetRepository { StubChartLoad* lastChart = nullptr; StubGridLoad* lastGrid = nullptr; data::ChartLoad* loadChartAsync(const std::string&) override { lastChart = new StubChartLoad; return lastChart; } data::GridLoad* loadGridAsync(const std::string&) override { lastGrid = new StubGridLoad; return lastGrid; } }; } TEST(DatasetDetailController, EmitsChartReadyOnDone) { StubAsyncRepo repo; auto reg = makeInversionRegistry(); controller::DatasetDetailController c(repo, reg); QSignalSpy spy(&c, &controller::DatasetDetailController::chartReady); c.openDataset("ds1", "dd_inversion_data"); repo.lastChart->fireDone(); EXPECT_EQ(spy.count(), 1); } TEST(DatasetDetailController, EmitsLoadFailedOnFailed) { StubAsyncRepo repo; auto reg = makeInversionRegistry(); controller::DatasetDetailController c(repo, reg); QSignalSpy spy(&c, &controller::DatasetDetailController::loadFailed); c.openDataset("ds1", "dd_inversion_data"); repo.lastChart->fireFailed(); EXPECT_EQ(spy.count(), 1); } TEST(DatasetDetailController, UnsupportedTypeFailsImmediately) { StubAsyncRepo repo; auto reg = makeInversionRegistry(); // 注册了反演,但未注册 dd_other controller::DatasetDetailController c(repo, reg); QSignalSpy spy(&c, &controller::DatasetDetailController::loadFailed); c.openDataset("ds1", "dd_other"); EXPECT_EQ(spy.count(), 1); EXPECT_EQ(repo.lastChart, nullptr); // 未发起加载 } // 空注册表 → 任意 ddCode 都不支持 → loadFailed,不发起加载。 TEST(DatasetDetailController, EmptyRegistryFailsAnyType) { StubAsyncRepo repo; controller::ChartStrategyRegistry reg; // 空 controller::DatasetDetailController c(repo, reg); QSignalSpy spy(&c, &controller::DatasetDetailController::loadFailed); c.openDataset("ds1", "dd_inversion_data"); EXPECT_EQ(spy.count(), 1); EXPECT_EQ(repo.lastChart, nullptr); } // 无网格阶段的策略 → loadGridData 不发起网格加载。 TEST(DatasetDetailController, NoGridPhaseStrategySkipsGridLoad) { StubAsyncRepo repo; controller::ChartStrategyRegistry reg; reg.add(std::make_unique()); controller::DatasetDetailController c(repo, reg); c.loadGridData("ds1", "dd_scatter_only"); EXPECT_EQ(repo.lastGrid, nullptr); // hasGridPhase()==false → 未发起 } TEST(DatasetDetailController, AbortsPreviousOnReopen) { StubAsyncRepo repo; auto reg = makeInversionRegistry(); controller::DatasetDetailController c(repo, reg); c.openDataset("dsA", "dd_inversion_data"); StubChartLoad* a = repo.lastChart; c.openDataset("dsB", "dd_inversion_data"); // 替换 EXPECT_TRUE(a->aborted); // 旧句柄被 abort } TEST(DatasetDetailController, DropsLateSignalFromAbortedLoad) { StubAsyncRepo repo; auto reg = makeInversionRegistry(); controller::DatasetDetailController c(repo, reg); QSignalSpy spy(&c, &controller::DatasetDetailController::chartReady); c.openDataset("dsA", "dd_inversion_data"); StubChartLoad* a = repo.lastChart; c.openDataset("dsB", "dd_inversion_data"); StubChartLoad* b = repo.lastChart; a->fireDone(); // 旧句柄迟到 → 身份比对丢弃 EXPECT_EQ(spy.count(), 0); b->fireDone(); // 当前句柄 → 正常 EXPECT_EQ(spy.count(), 1); } TEST(DatasetDetailController, EmitsGridReadyOnDone) { StubAsyncRepo repo; auto reg = makeInversionRegistry(); controller::DatasetDetailController c(repo, reg); QSignalSpy spy(&c, &controller::DatasetDetailController::gridReady); c.loadGridData("ds1", "dd_inversion_data"); repo.lastGrid->fireDone(); EXPECT_EQ(spy.count(), 1); } TEST(DatasetDetailController, EmitsLoadFailedOnGridFailed) { StubAsyncRepo repo; auto reg = makeInversionRegistry(); controller::DatasetDetailController c(repo, reg); QSignalSpy spy(&c, &controller::DatasetDetailController::loadFailed); c.loadGridData("ds1", "dd_inversion_data"); repo.lastGrid->fireFailed(); EXPECT_EQ(spy.count(), 1); } TEST(DatasetDetailController, DropsLateGridSignalFromAbortedLoad) { StubAsyncRepo repo; auto reg = makeInversionRegistry(); controller::DatasetDetailController c(repo, reg); QSignalSpy spy(&c, &controller::DatasetDetailController::gridReady); c.loadGridData("dsA", "dd_inversion_data"); StubGridLoad* a = repo.lastGrid; c.loadGridData("dsB", "dd_inversion_data"); // 替换 → 旧句柄被 abort StubGridLoad* b = repo.lastGrid; EXPECT_TRUE(a->aborted); a->fireDone(); // 旧句柄迟到 → 身份比对丢弃 EXPECT_EQ(spy.count(), 0); b->fireDone(); // 当前句柄 → 正常 EXPECT_EQ(spy.count(), 1); }