Extra2D/tests/test_plugin_loader.cpp

414 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <gtest/gtest.h>
#include <plugin/plugin_loader.h>
#include <plugin/iplugin.h>
#include <algorithm>
using namespace extra2d;
// ============================================================================
// 测试用插件类
// ============================================================================
class TestPlugin : public IPlugin {
public:
TestPlugin(const char* name, const char* version = "1.0.0",
const std::vector<std::string>& deps = {})
: name_(name), version_(version), dependencies_(deps) {
info_.name = name;
info_.version = version;
info_.dependencies = deps;
}
const PluginInfo& getInfo() const override { return info_; }
bool load() override {
loadCalled = true;
return loadResult;
}
void unload() override {
unloadCalled = true;
}
std::vector<std::string> getDependencies() const override {
return dependencies_;
}
std::string name_;
std::string version_;
std::vector<std::string> dependencies_;
PluginInfo info_;
bool loadCalled = false;
bool unloadCalled = false;
bool loadResult = true;
};
// ============================================================================
// PluginLoader 基础测试
// ============================================================================
TEST(PluginLoader, BasicOperations) {
PluginLoader loader;
TestPlugin plugin1("Plugin1");
TestPlugin plugin2("Plugin2", "2.0.0");
// 注册插件
loader.registerPlugin(&plugin1);
EXPECT_EQ(1, loader.getPluginCount());
EXPECT_TRUE(loader.hasPlugin("Plugin1"));
loader.registerPlugin(&plugin2);
EXPECT_EQ(2, loader.getPluginCount());
EXPECT_TRUE(loader.hasPlugin("Plugin2"));
// 获取插件
EXPECT_EQ(&plugin1, loader.getPlugin("Plugin1"));
EXPECT_EQ(&plugin2, loader.getPlugin("Plugin2"));
EXPECT_EQ(nullptr, loader.getPlugin("NonExistent"));
// 卸载插件
loader.unloadPlugin("Plugin1");
EXPECT_EQ(1, loader.getPluginCount());
EXPECT_FALSE(loader.hasPlugin("Plugin1"));
EXPECT_TRUE(loader.hasPlugin("Plugin2"));
}
TEST(PluginLoader, DuplicateRegistration) {
PluginLoader loader;
TestPlugin plugin1("SameName", "1.0.0");
TestPlugin plugin2("SameName", "2.0.0");
loader.registerPlugin(&plugin1);
EXPECT_EQ(1, loader.getPluginCount());
// 重复注册同名插件,后注册的应覆盖
loader.registerPlugin(&plugin2);
EXPECT_EQ(1, loader.getPluginCount());
IPlugin* retrieved = loader.getPlugin("SameName");
EXPECT_NE(nullptr, retrieved);
EXPECT_EQ("2.0.0", retrieved->getInfo().version);
}
TEST(PluginLoader, GetAllPlugins) {
PluginLoader loader;
TestPlugin plugin1("Plugin1");
TestPlugin plugin2("Plugin2");
TestPlugin plugin3("Plugin3");
loader.registerPlugin(&plugin1);
loader.registerPlugin(&plugin2);
loader.registerPlugin(&plugin3);
auto allPlugins = loader.getAllPlugins();
EXPECT_EQ(3, allPlugins.size());
}
// ============================================================================
// 加载和卸载测试
// ============================================================================
TEST(PluginLoader, LoadUnload) {
PluginLoader loader;
TestPlugin plugin1("Plugin1");
TestPlugin plugin2("Plugin2");
loader.registerPlugin(&plugin1);
loader.registerPlugin(&plugin2);
// 加载所有插件
EXPECT_TRUE(loader.initAll());
EXPECT_TRUE(plugin1.loadCalled);
EXPECT_TRUE(plugin2.loadCalled);
// 卸载所有插件
loader.shutdownAll();
EXPECT_TRUE(plugin1.unloadCalled);
EXPECT_TRUE(plugin2.unloadCalled);
}
TEST(PluginLoader, LoadFailure) {
PluginLoader loader;
TestPlugin plugin1("Plugin1");
TestPlugin plugin2("Plugin2");
plugin2.loadResult = false; // 模拟加载失败
loader.registerPlugin(&plugin1);
loader.registerPlugin(&plugin2);
// 加载应失败
EXPECT_FALSE(loader.initAll());
EXPECT_TRUE(plugin1.loadCalled);
EXPECT_TRUE(plugin2.loadCalled);
}
// ============================================================================
// 依赖关系测试
// ============================================================================
TEST(PluginLoader, Dependencies) {
PluginLoader loader;
// PluginC 依赖 PluginBPluginB 依赖 PluginA
TestPlugin pluginA("PluginA");
TestPlugin pluginB("PluginB", "1.0.0", {"PluginA"});
TestPlugin pluginC("PluginC", "1.0.0", {"PluginB"});
loader.registerPlugin(&pluginC);
loader.registerPlugin(&pluginB);
loader.registerPlugin(&pluginA);
// 所有插件都应能加载(依赖会自动解析)
EXPECT_TRUE(loader.initAll());
EXPECT_TRUE(pluginA.loadCalled);
EXPECT_TRUE(pluginB.loadCalled);
EXPECT_TRUE(pluginC.loadCalled);
loader.shutdownAll();
}
TEST(PluginLoader, MissingDependency) {
PluginLoader loader;
// PluginB 依赖不存在的 PluginA
TestPlugin pluginB("PluginB", "1.0.0", {"PluginA"});
loader.registerPlugin(&pluginB);
// 加载应失败(依赖未满足)
EXPECT_FALSE(loader.initAll());
EXPECT_FALSE(pluginB.loadCalled);
}
TEST(PluginLoader, CircularDependency) {
PluginLoader loader;
// A 依赖 BB 依赖 A循环依赖
TestPlugin pluginA("PluginA", "1.0.0", {"PluginB"});
TestPlugin pluginB("PluginB", "1.0.0", {"PluginA"});
loader.registerPlugin(&pluginA);
loader.registerPlugin(&pluginB);
// 循环依赖应能处理(目前实现可能不检测循环,但至少不应崩溃)
// 注意:根据实现不同,这可能成功或失败
loader.initAll();
loader.shutdownAll();
}
TEST(PluginLoader, DependencyLoadOrder) {
PluginLoader loader;
std::vector<std::string> loadOrder;
std::vector<std::string> unloadOrder;
class OrderTrackingPlugin : public IPlugin {
public:
OrderTrackingPlugin(const char* name,
const std::vector<std::string>& deps,
std::vector<std::string>& loadVec,
std::vector<std::string>& unloadVec)
: name_(name), loadOrder_(loadVec), unloadOrder_(unloadVec) {
info_.name = name;
info_.dependencies = deps;
}
const PluginInfo& getInfo() const override { return info_; }
bool load() override {
loadOrder_.push_back(name_);
return true;
}
void unload() override {
unloadOrder_.push_back(name_);
}
std::string name_;
PluginInfo info_;
std::vector<std::string>& loadOrder_;
std::vector<std::string>& unloadOrder_;
};
// C 依赖 BB 依赖 A
OrderTrackingPlugin pluginC("PluginC", {"PluginB"}, loadOrder, unloadOrder);
OrderTrackingPlugin pluginA("PluginA", {}, loadOrder, unloadOrder);
OrderTrackingPlugin pluginB("PluginB", {"PluginA"}, loadOrder, unloadOrder);
loader.registerPlugin(&pluginC);
loader.registerPlugin(&pluginA);
loader.registerPlugin(&pluginB);
loader.initAll();
// 加载顺序应满足依赖关系
EXPECT_EQ(3, loadOrder.size());
// A 必须在 B 之前加载
auto aPos = std::find(loadOrder.begin(), loadOrder.end(), "PluginA");
auto bPos = std::find(loadOrder.begin(), loadOrder.end(), "PluginB");
auto cPos = std::find(loadOrder.begin(), loadOrder.end(), "PluginC");
EXPECT_TRUE(aPos < bPos);
EXPECT_TRUE(bPos < cPos);
loader.shutdownAll();
// 卸载顺序应与加载顺序相反
EXPECT_EQ(3, unloadOrder.size());
}
// ============================================================================
// 空加载器测试
// ============================================================================
TEST(PluginLoader, EmptyLoader) {
PluginLoader loader;
EXPECT_EQ(0, loader.getPluginCount());
EXPECT_FALSE(loader.hasPlugin("AnyPlugin"));
EXPECT_EQ(nullptr, loader.getPlugin("AnyPlugin"));
auto allPlugins = loader.getAllPlugins();
EXPECT_EQ(0, allPlugins.size());
// 空加载器的初始化和关闭不应崩溃
EXPECT_TRUE(loader.initAll());
loader.shutdownAll();
}
TEST(PluginLoader, UnloadNonExistent) {
PluginLoader loader;
// 卸载不存在的插件不应崩溃
EXPECT_NO_THROW(loader.unloadPlugin("NonExistent"));
}
// ============================================================================
// 搜索路径测试
// ============================================================================
TEST(PluginLoader, SearchPaths) {
PluginLoader loader;
// 添加搜索路径
loader.addSearchPath("/usr/lib/extra2d/plugins");
loader.addSearchPath("/home/user/.extra2d/plugins");
loader.addSearchPath("./plugins");
// 搜索路径功能主要用于动态库加载,这里只验证不崩溃
EXPECT_NO_THROW(loader.loadPluginsFromDirectory("./non_existent_dir"));
}
// ============================================================================
// 插件信息测试
// ============================================================================
TEST(PluginLoader, PluginInfo) {
PluginLoader loader;
TestPlugin plugin("TestPlugin", "1.2.3");
plugin.info_.author = "Test Author";
plugin.info_.description = "Test Description";
plugin.info_.dependencies = {"Dep1", "Dep2"};
loader.registerPlugin(&plugin);
IPlugin* retrieved = loader.getPlugin("TestPlugin");
EXPECT_NE(nullptr, retrieved);
const PluginInfo& info = retrieved->getInfo();
EXPECT_EQ("TestPlugin", info.name);
EXPECT_EQ("1.2.3", info.version);
EXPECT_EQ("Test Author", info.author);
EXPECT_EQ("Test Description", info.description);
EXPECT_EQ(2, info.dependencies.size());
EXPECT_EQ("Dep1", info.dependencies[0]);
EXPECT_EQ("Dep2", info.dependencies[1]);
}
// ============================================================================
// 插件生命周期测试
// ============================================================================
TEST(PluginLoader, PluginLifecycle) {
PluginLoader loader;
enum class State { Created, Loaded, Running, Unloaded };
class LifecyclePlugin : public IPlugin {
public:
LifecyclePlugin(const char* name, State& state)
: name_(name), state_(state) {
state_ = State::Created;
info_.name = name;
}
const PluginInfo& getInfo() const override { return info_; }
bool load() override {
state_ = State::Loaded;
return true;
}
void unload() override {
state_ = State::Unloaded;
}
std::string name_;
State& state_;
PluginInfo info_;
};
State state = State::Created;
LifecyclePlugin plugin("Lifecycle", state);
EXPECT_EQ(State::Created, state);
loader.registerPlugin(&plugin);
loader.initAll();
EXPECT_EQ(State::Loaded, state);
loader.shutdownAll();
EXPECT_EQ(State::Unloaded, state);
}
// ============================================================================
// 复杂依赖测试
// ============================================================================
TEST(PluginLoader, ComplexDependencies) {
PluginLoader loader;
// 创建复杂的依赖图:
// A -> B -> D
// A -> C -> D
// E (独立)
TestPlugin pluginA("A", "1.0.0", {});
TestPlugin pluginB("B", "1.0.0", {"A"});
TestPlugin pluginC("C", "1.0.0", {"A"});
TestPlugin pluginD("D", "1.0.0", {"B", "C"});
TestPlugin pluginE("E", "1.0.0", {});
// 乱序注册
loader.registerPlugin(&pluginD);
loader.registerPlugin(&pluginB);
loader.registerPlugin(&pluginE);
loader.registerPlugin(&pluginC);
loader.registerPlugin(&pluginA);
// 所有插件都应能加载
EXPECT_TRUE(loader.initAll());
EXPECT_TRUE(pluginA.loadCalled);
EXPECT_TRUE(pluginB.loadCalled);
EXPECT_TRUE(pluginC.loadCalled);
EXPECT_TRUE(pluginD.loadCalled);
EXPECT_TRUE(pluginE.loadCalled);
loader.shutdownAll();
}