* Copyright (c) 2024 Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include <chrono>
#include <iostream>
#include <mc/exception.h>
#include <mc/log.h>
#include <mc/module.h>
#include <mc/variant.h>
#include <thread>
* @brief 测试 devices 模块加载
* @return 测试是否成功
*/
bool test_devices_module()
{
try {
ilog("=== 1. Verify devices module load ===");
auto devices_module = mc::load_module("mc.devices");
if (!devices_module) {
elog("cannot load devices module");
return false;
}
ilog("successfully loaded module: ${name} v${version}",
("name", devices_module->name())("version", devices_module->version()));
auto devices_factory = devices_module->get_factory();
auto sensor_obj = devices_factory->try_create_object("Sensor");
if (sensor_obj) {
sensor_obj->invoke_method("setName", {mc::variant("test_sensor")});
auto name_result = sensor_obj->invoke_method("getName", {});
ilog("✓ devices module sensor test succeeded: ${name}", ("name", name_result.as<std::string>()));
}
return true;
} catch (const mc::exception& e) {
elog("devices module test failed: ${msg}", ("msg", e.what()));
return false;
} catch (const std::exception& e) {
elog("devices module test failed: ${msg}", ("msg", e.what()));
return false;
}
}
* @brief 测试 network 模块加载
* @return 测试是否成功
*/
bool test_network_module()
{
try {
ilog("=== 2. Verify network module load ===");
auto network_module = mc::load_module("mc.network");
if (!network_module) {
elog("cannot load network module");
return false;
}
ilog("successfully loaded network module: ${name}", ("name", network_module->name()));
auto network_factory = network_module->get_factory();
auto client_obj = network_factory->try_create_object("NetworkClient");
if (client_obj) {
auto connect_result = client_obj->invoke_method("connect", {mc::variant("127.0.0.1"), mc::variant(8080)});
ilog("✓ network module connection test: ${result}", ("result", connect_result.as<bool>()));
auto host_result = client_obj->invoke_method("getHost", {});
ilog("✓ connected host: ${host}", ("host", host_result.as<std::string>()));
}
return true;
} catch (const mc::exception& e) {
elog("network module test failed: ${msg}", ("msg", e.what()));
return false;
} catch (const std::exception& e) {
elog("network module test failed: ${msg}", ("msg", e.what()));
return false;
}
}
* @brief 测试 protocol 模块加载
* @return 测试是否成功
*/
bool test_protocol_module()
{
try {
ilog("=== 3. Verify protocol module load (init.so mode) ===");
auto protocol_module = mc::load_module("mc.protocol");
if (!protocol_module) {
elog("cannot load protocol module");
return false;
}
ilog("successfully loaded protocol module: ${name}", ("name", protocol_module->name()));
auto protocol_factory = protocol_module->get_factory();
auto handler_obj = protocol_factory->try_create_object("ProtocolHandler");
if (handler_obj) {
auto request_result = handler_obj->invoke_method("handleRequest", {mc::variant("test request")});
ilog("✓ protocol module handled request: ${result}", ("result", request_result.as<std::string>()));
auto version_result = handler_obj->invoke_method("getVersionString", {});
ilog("✓ protocol version: ${version}", ("version", version_result.as<std::string>()));
}
return true;
} catch (const mc::exception& e) {
elog("protocol module test failed: ${msg}", ("msg", e.what()));
return false;
} catch (const std::exception& e) {
elog("protocol module test failed: ${msg}", ("msg", e.what()));
return false;
}
}
* @brief 测试 database 模块加载
* @return 测试是否成功
*/
bool test_database_module()
{
try {
ilog("=== 4. Verify nested-path database module load ===");
auto database_module = mc::load_module("mc.test.database");
if (!database_module) {
elog("cannot load mc.test.database module");
return false;
}
ilog("successfully loaded database module: ${name}", ("name", database_module->name()));
auto database_factory = database_module->get_factory();
auto db_obj = database_factory->try_create_object("DbConnection");
if (db_obj) {
auto connect_result = db_obj->invoke_method("connect", {mc::variant("localhost"), mc::variant("test_db")});
ilog("✓ database module connection test: ${result}", ("result", connect_result.as<bool>()));
auto db_name_result = db_obj->invoke_method("getDatabaseName", {});
ilog("✓ database name: ${name}", ("name", db_name_result.as<std::string>()));
auto query_result = db_obj->invoke_method("executeQuery", {mc::variant("SELECT * FROM test")});
auto results = query_result.as<std::vector<std::string>>();
if (!results.empty()) {
ilog("✓ query result: ${result}", ("result", results[0]));
}
}
return true;
} catch (const mc::exception& e) {
elog("database module test failed: ${msg}", ("msg", e.what()));
return false;
} catch (const std::exception& e) {
elog("database module test failed: ${msg}", ("msg", e.what()));
return false;
}
}
* @brief 模块路径查找验证总结
*/
void test_module_path_summary()
{
ilog("=== 5. Module path lookup summary ===");
ilog("✓ mc.devices -> modules/mc/devices.so (standard mode)");
ilog("✓ mc.network -> modules/mc/network.so (standard mode)");
ilog("✓ mc.protocol -> modules/mc/protocol/init.so (init.so mode)");
ilog("✓ mc.test.database -> modules/mc/test/database.so (deep path mode)");
ilog("all module path lookup tests completed!");
}
* @brief 测试模块两阶段卸载机制
* @return 测试是否成功
*/
bool test_two_phase_unload()
{
try {
ilog("=== 6. Two-phase module unload verification ===");
ilog("Verify unload() only decreases refcount; actual unload requires releasing all module_ptr");
auto& module_manager = mc::get_module_manager();
ilog("=== 6.1 Save module ref for two-phase unload ===");
auto devices_module_ref = mc::load_module("mc.devices");
auto network_module_ref = mc::load_module("mc.network");
if (devices_module_ref) {
auto factory = devices_module_ref->get_factory();
auto temp_obj = factory->try_create_object("Sensor");
if (temp_obj) {
ilog("devices module loaded");
}
}
if (network_module_ref) {
auto factory = network_module_ref->get_factory();
auto temp_obj = factory->try_create_object("NetworkClient");
if (temp_obj) {
ilog("network module loaded");
}
}
ilog("=== 6.2 Phase 1: call unload() ===");
ilog("Before unload(), devices_module_ref refcount: ${count}", ("count", devices_module_ref.use_count()));
ilog("Before unload(), network_module_ref refcount: ${count}", ("count", network_module_ref.use_count()));
ilog("Calling module_manager.unload('mc.devices')...");
module_manager.unload("mc.devices");
ilog("After unload(), devices_module_ref refcount: ${count}", ("count", devices_module_ref.use_count()));
bool devices_still_loaded = module_manager.is_loaded("mc.devices");
ilog("module manager status: ${status}", ("status", devices_still_loaded ? "loaded" : "not loaded"));
if (devices_module_ref) {
auto factory = devices_module_ref->get_factory();
auto sensor_obj = factory->try_create_object("Sensor");
if (sensor_obj) {
sensor_obj->invoke_method("setName", {mc::variant("after_unload_test")});
auto name_result = sensor_obj->invoke_method("getName", {});
ilog("✓ module reference still available: ${name}", ("name", name_result.as<std::string>()));
ilog("✓ verification succeeded: after phase-1 unload, modules holding references remain usable");
}
}
ilog("=== 6.3 Phase 2: release module_ptr ref ===");
ilog("Releasing devices_module_ref...");
ilog("Refcount before release: ${count}", ("count", devices_module_ref.use_count()));
ilog("Watch console output; expect '[DEVICES MODULE] shared library actually unloaded!'");
devices_module_ref.reset();
std::cout.flush();
ilog("devices_module_ref released");
ilog("Releasing network_module_ref...");
ilog("Refcount before release: ${count}", ("count", network_module_ref.use_count()));
ilog("Watch console output; expect '[NETWORK MODULE] shared library actually unloaded!'");
network_module_ref.reset();
std::cout.flush();
ilog("network_module_ref already released");
ilog("=== 6.4 Verify actual reload ===");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
ilog("Reloading devices module...");
ilog("Watch console output; expect new '[DEVICES MODULE] module loaded'");
auto reloaded_devices = mc::load_module("mc.devices");
if (reloaded_devices) {
ilog("✓ verification succeeded: module was actually reloaded");
ilog(" Watch console module load/unload messages to verify the actual unload");
auto factory = reloaded_devices->get_factory();
auto sensor_obj = factory->try_create_object("Sensor");
if (sensor_obj) {
sensor_obj->invoke_method("setName", {mc::variant("reloaded_after_real_unload")});
auto name_result = sensor_obj->invoke_method("getName", {});
ilog("✓ reloaded module works: ${name}", ("name", name_result.as<std::string>()));
}
} else {
elog("✗ reload failed");
return false;
}
ilog("=== 6.5 Clean up other modules ===");
module_manager.unload("mc.protocol");
module_manager.unload("mc.test.database");
ilog("=== 6.6 Final state check ===");
std::vector<std::string> all_modules = {"mc.devices", "mc.network", "mc.protocol", "mc.test.database"};
for (const auto& module_name : all_modules) {
bool is_loaded = module_manager.is_loaded(module_name);
ilog(" ${name}: ${status}", ("name", module_name)("status", is_loaded ? "loaded" : "not loaded"));
}
ilog("=== Two-phase module unload verification done ===");
ilog("✓ verified unload() is only a logical unload");
ilog("✓ verified module holding module_ptr is still available");
ilog("✓ verified the shared library is actually unloaded after all references are released");
ilog("✓ verified reload actually reinitializes the module");
return true;
} catch (const mc::exception& e) {
elog("two-phase unload test failed: ${msg}", ("msg", e.what()));
return false;
} catch (const std::exception& e) {
elog("two-phase unload test failed: ${msg}", ("msg", e.what()));
return false;
}
}
* @brief 演示模块系统
*/
int main()
{
try {
ilog("starting module loading demo");
bool success = true;
success &= test_devices_module();
success &= test_network_module();
success &= test_protocol_module();
success &= test_database_module();
test_module_path_summary();
success &= test_two_phase_unload();
if (success) {
ilog("all tests completed!");
return 0;
} else {
elog("Some tests failed");
return -1;
}
} catch (const mc::exception& e) {
elog("exception: ${msg}", ("msg", e.what()));
return -1;
} catch (const std::exception& e) {
elog("standard exception: ${msg}", ("msg", e.what()));
return -1;
}
}