178 lines
7.1 KiB
C++
178 lines
7.1 KiB
C++
#include <gtest/gtest.h>
|
||
#include <QSignalSpy>
|
||
#include <QVariant>
|
||
#include "DatasetDetailController.hpp"
|
||
#include "DatasetDetailTab.hpp"
|
||
#include "IDatasetChartStrategy.hpp"
|
||
#include "repo/IAsyncDatasetRepository.hpp"
|
||
#include "api/DatasetLoadHandles.hpp"
|
||
using namespace geopro;
|
||
|
||
namespace {
|
||
// 反演策略桩:散点(非 lazy) + 网格(lazy) 两页签。
|
||
struct InversionStrategy : controller::IDatasetChartStrategy {
|
||
std::string ddCode() const override { return "dd_inversion_data"; }
|
||
std::vector<controller::TabSpec> tabs() const override {
|
||
return {
|
||
{QStringLiteral("原数据"), controller::ViewKind::Scatter,
|
||
QStringLiteral("inversion.scatter"), /*lazy*/ false, /*paginated*/ false},
|
||
{QStringLiteral("网格数据"), controller::ViewKind::FilledContour,
|
||
QStringLiteral("inversion.grid"), /*lazy*/ true, /*paginated*/ false},
|
||
};
|
||
}
|
||
};
|
||
// 单页签策略桩(仅一个非 lazy 页签)。
|
||
struct SingleTabStrategy : controller::IDatasetChartStrategy {
|
||
std::string ddCode() const override { return "dd_scatter_only"; }
|
||
std::vector<controller::TabSpec> tabs() const override {
|
||
return {{QStringLiteral("散点"), controller::ViewKind::Scatter,
|
||
QStringLiteral("scatter.only"), /*lazy*/ false, /*paginated*/ false}};
|
||
}
|
||
};
|
||
// 注册了反演策略的注册表(多数用例复用)。
|
||
controller::ChartStrategyRegistry makeInversionRegistry() {
|
||
controller::ChartStrategyRegistry reg;
|
||
reg.add(std::make_unique<InversionStrategy>());
|
||
return reg;
|
||
}
|
||
|
||
// 桩详情句柄:不声明 Q_OBJECT —— 发射继承自 data::DetailLoad 的信号、override abort。
|
||
struct StubDetailLoad : data::DetailLoad {
|
||
bool aborted = false;
|
||
void abort() override { aborted = true; }
|
||
void fireDone() { emit done(QVariant{}); }
|
||
void fireFailed() { emit failed(QStringLiteral("x")); }
|
||
};
|
||
// 桩仓储:每个 loaderKey 都造一个新句柄,记录最近一个用于 fire。
|
||
struct StubAsyncRepo : data::IAsyncDatasetRepository {
|
||
StubDetailLoad* last = nullptr;
|
||
data::DetailLoad* loadAsync(const std::string&, const std::string&) override {
|
||
last = new StubDetailLoad;
|
||
return last;
|
||
}
|
||
};
|
||
} // namespace
|
||
|
||
TEST(DatasetDetailController, OpenEmitsDatasetOpenedWithTabsAndDdCode) {
|
||
StubAsyncRepo repo;
|
||
auto reg = makeInversionRegistry();
|
||
controller::DatasetDetailController c(repo, reg);
|
||
QSignalSpy spy(&c, &controller::DatasetDetailController::datasetOpened);
|
||
c.openDataset("ds1", "dd_inversion_data", "名称");
|
||
ASSERT_EQ(spy.count(), 1);
|
||
const auto args = spy.takeFirst();
|
||
EXPECT_EQ(args.at(0).toString(), QStringLiteral("ds1"));
|
||
EXPECT_EQ(args.at(1).toString(), QStringLiteral("dd_inversion_data")); // ddCode 透传
|
||
EXPECT_EQ(args.at(2).toString(), QStringLiteral("名称"));
|
||
}
|
||
|
||
TEST(DatasetDetailController, OpenLoadsNonLazyTabsOnly) {
|
||
StubAsyncRepo repo;
|
||
auto reg = makeInversionRegistry();
|
||
controller::DatasetDetailController c(repo, reg);
|
||
QSignalSpy started(&c, &controller::DatasetDetailController::tabLoadStarted);
|
||
c.openDataset("ds1", "dd_inversion_data");
|
||
// 反演:tab0 非 lazy 自动加载,tab1 lazy 不加载。
|
||
ASSERT_EQ(started.count(), 1);
|
||
EXPECT_EQ(started.takeFirst().at(1).toInt(), 0);
|
||
}
|
||
|
||
TEST(DatasetDetailController, EmitsTabReadyOnDone) {
|
||
StubAsyncRepo repo;
|
||
auto reg = makeInversionRegistry();
|
||
controller::DatasetDetailController c(repo, reg);
|
||
QSignalSpy spy(&c, &controller::DatasetDetailController::tabReady);
|
||
c.openDataset("ds1", "dd_inversion_data");
|
||
repo.last->fireDone();
|
||
ASSERT_EQ(spy.count(), 1);
|
||
EXPECT_EQ(spy.takeFirst().at(1).toInt(), 0); // tabIndex 0
|
||
}
|
||
|
||
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.last->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.last, 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.last, nullptr);
|
||
}
|
||
|
||
// 越界 tabIndex → loadTab 静默不加载。
|
||
TEST(DatasetDetailController, LoadTabOutOfRangeDoesNothing) {
|
||
StubAsyncRepo repo;
|
||
auto reg = makeInversionRegistry();
|
||
controller::DatasetDetailController c(repo, reg);
|
||
c.loadTab("ds1", "dd_inversion_data", 99);
|
||
EXPECT_EQ(repo.last, nullptr);
|
||
}
|
||
|
||
// lazy 页签经 loadTab 触发后加载。
|
||
TEST(DatasetDetailController, LoadTabLazyTabStartsLoad) {
|
||
StubAsyncRepo repo;
|
||
auto reg = makeInversionRegistry();
|
||
controller::DatasetDetailController c(repo, reg);
|
||
QSignalSpy ready(&c, &controller::DatasetDetailController::tabReady);
|
||
c.loadTab("ds1", "dd_inversion_data", 1); // lazy 网格页
|
||
ASSERT_NE(repo.last, nullptr);
|
||
repo.last->fireDone();
|
||
ASSERT_EQ(ready.count(), 1);
|
||
EXPECT_EQ(ready.takeFirst().at(1).toInt(), 1);
|
||
}
|
||
|
||
TEST(DatasetDetailController, AbortsPreviousOnSameSlotReload) {
|
||
StubAsyncRepo repo;
|
||
auto reg = makeInversionRegistry();
|
||
controller::DatasetDetailController c(repo, reg);
|
||
c.loadTab("dsA", "dd_inversion_data", 0);
|
||
StubDetailLoad* a = repo.last;
|
||
c.loadTab("dsB", "dd_inversion_data", 0); // 同槽位替换
|
||
EXPECT_TRUE(a->aborted); // 旧句柄被 abort
|
||
}
|
||
|
||
TEST(DatasetDetailController, DropsLateSignalFromAbortedLoad) {
|
||
StubAsyncRepo repo;
|
||
auto reg = makeInversionRegistry();
|
||
controller::DatasetDetailController c(repo, reg);
|
||
QSignalSpy spy(&c, &controller::DatasetDetailController::tabReady);
|
||
c.loadTab("dsA", "dd_inversion_data", 0);
|
||
StubDetailLoad* a = repo.last;
|
||
c.loadTab("dsB", "dd_inversion_data", 0); // 同槽位替换
|
||
StubDetailLoad* b = repo.last;
|
||
a->fireDone(); // 旧句柄迟到 → 身份比对丢弃
|
||
EXPECT_EQ(spy.count(), 0);
|
||
b->fireDone(); // 当前句柄 → 正常
|
||
EXPECT_EQ(spy.count(), 1);
|
||
}
|
||
|
||
TEST(DatasetDetailController, FocusEmitsFocusRequested) {
|
||
StubAsyncRepo repo;
|
||
auto reg = makeInversionRegistry();
|
||
controller::DatasetDetailController c(repo, reg);
|
||
QSignalSpy spy(&c, &controller::DatasetDetailController::focusRequested);
|
||
c.focusDataset("ds1");
|
||
ASSERT_EQ(spy.count(), 1);
|
||
EXPECT_EQ(spy.takeFirst().at(0).toString(), QStringLiteral("ds1"));
|
||
}
|