optimal & fix file cmake
This commit is contained in:
287
test/plugin_registry_test.cpp
Normal file
287
test/plugin_registry_test.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* Kiểm đường nạp plugin thật: YAML -> library_path -> Boost.DLL -> schema.
|
||||
*
|
||||
* Test này cần PNKX_NAV_CORE_CONFIG_DIR trỏ vào test/config của gói:
|
||||
*
|
||||
* PNKX_NAV_CORE_CONFIG_DIR=src/AMR_T800/Test/mission_adapters/test/config \
|
||||
* ./devel/lib/mission_adapters/plugin_registry_test
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <robot/robot.h>
|
||||
|
||||
#include <mission_adapters/mission_adapters.h>
|
||||
#include <mission_adapters/plugin_registry.h>
|
||||
|
||||
#include "mission_test_utils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace mission_adapters;
|
||||
|
||||
/**
|
||||
* @brief Nguồn mission thứ ba, viết hoàn toàn trong test.
|
||||
*
|
||||
* Sự tồn tại của nó là bài kiểm tra thật cho tính plugin: thêm một loại nguồn mới mà không sửa file
|
||||
* nào trong `src/` của gói.
|
||||
*/
|
||||
class DummySourceAdapter : public MissionSourceAdapter
|
||||
{
|
||||
public:
|
||||
static constexpr const char* kSchema = "test.dummy";
|
||||
|
||||
bool configure(const std::string& name, robot::NodeHandle& nh) override
|
||||
{
|
||||
(void)nh;
|
||||
name_ = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string schema() const override { return kSchema; }
|
||||
|
||||
bool validate(const MissionRequest& request, std::string& reason) const override
|
||||
{
|
||||
if (request.raw_payload.empty())
|
||||
{
|
||||
reason = "raw_payload is empty";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ConversionResult convert(const MissionRequest& request) override
|
||||
{
|
||||
(void)request;
|
||||
auto mission = std::make_shared<Mission>();
|
||||
mission->type = MissionType::SIMPLE_GOAL;
|
||||
|
||||
ConversionResult result;
|
||||
result.missions.push_back(mission);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
class PluginRegistryTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
robot::NodeHandle nh;
|
||||
};
|
||||
|
||||
// ── Đường nạp bình thường ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
TEST_F(PluginRegistryTest, LoadsBothSourcesFromConfig)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
|
||||
ASSERT_TRUE(registry.loadFromConfig(nh))
|
||||
<< "could not load the plugin — check PNKX_NAV_CORE_CONFIG_DIR and devel/lib";
|
||||
|
||||
EXPECT_EQ(registry.size(), 2u);
|
||||
EXPECT_NE(registry.find(schema::kPoseStamped), nullptr);
|
||||
EXPECT_NE(registry.find(schema::kVda5050Order), nullptr);
|
||||
EXPECT_EQ(registry.find("does.not.exist"), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(PluginRegistryTest, LoadedAdapterConvertsRealPayload)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
ASSERT_TRUE(registry.loadFromConfig(nh));
|
||||
|
||||
auto* adapter = registry.find(schema::kPoseStamped);
|
||||
ASSERT_NE(adapter, nullptr);
|
||||
|
||||
const auto request = MissionRequest::fromPose(mission_test::makeGoal(3.0, 4.0));
|
||||
|
||||
std::string reason;
|
||||
ASSERT_TRUE(adapter->validate(request, reason)) << reason;
|
||||
|
||||
const auto result = adapter->convert(request);
|
||||
ASSERT_EQ(result.missions.size(), 1u);
|
||||
EXPECT_DOUBLE_EQ(result.missions.front()->goal.pose.position.x, 3.0);
|
||||
}
|
||||
|
||||
// ── Các cách hỏng, đều phải báo lỗi rõ ràng chứ không crash ──────────────────────────────────────
|
||||
|
||||
TEST_F(PluginRegistryTest, MissingLibraryPathKeyFailsCleanly)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
|
||||
EXPECT_FALSE(registry.loadFromConfig(nh, "registry_test_missing_library_path"));
|
||||
EXPECT_EQ(registry.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(PluginRegistryTest, MissingLibraryFileFailsCleanly)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
|
||||
EXPECT_FALSE(registry.loadFromConfig(nh, "registry_test_missing_library_file"));
|
||||
EXPECT_EQ(registry.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(PluginRegistryTest, WrongSymbolNameFailsCleanly)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
|
||||
// Thư viện có thật, symbol thì không: import_alias ném system_error, registry phải nuốt và báo.
|
||||
EXPECT_FALSE(registry.loadFromConfig(nh, "registry_test_wrong_symbol"));
|
||||
EXPECT_EQ(registry.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(PluginRegistryTest, DuplicateSchemaIsRejected)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
|
||||
// Hai instance cùng khai schema "geometry.pose_stamped": cái thứ hai bị từ chối thay vì ghi đè
|
||||
// im lặng, vì định tuyến khi đó sẽ phụ thuộc thứ tự nạp.
|
||||
EXPECT_FALSE(registry.loadFromConfig(nh, "registry_test_duplicate_schema"));
|
||||
EXPECT_EQ(registry.size(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(PluginRegistryTest, EntryWithoutTypeFailsCleanly)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
|
||||
EXPECT_FALSE(registry.loadFromConfig(nh, "registry_test_entry_without_type"));
|
||||
EXPECT_EQ(registry.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(PluginRegistryTest, MissingSourceListFailsCleanly)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
|
||||
EXPECT_FALSE(registry.loadFromConfig(nh, "registry_test_empty"));
|
||||
EXPECT_EQ(registry.size(), 0u);
|
||||
}
|
||||
|
||||
// ── Đăng ký trực tiếp (nguồn biên dịch thẳng vào host, hoặc test) ───────────────────────────────
|
||||
|
||||
TEST_F(PluginRegistryTest, RejectsNullAndDuplicateManualRegistration)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
|
||||
EXPECT_FALSE(registry.registerAdapter(nullptr));
|
||||
|
||||
auto first = std::make_shared<DummySourceAdapter>();
|
||||
EXPECT_TRUE(registry.registerAdapter(first));
|
||||
|
||||
auto second = std::make_shared<DummySourceAdapter>();
|
||||
EXPECT_FALSE(registry.registerAdapter(second)) << "two adapters with the same schema were both "
|
||||
"accepted";
|
||||
|
||||
EXPECT_EQ(registry.size(), 1u);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nguồn thứ ba đi hết đường: đăng ký -> EventProcessor định tuyến theo schema -> mission vào hàng
|
||||
* đợi. Không có dòng nào trong `src/` của gói biết tới DummySourceAdapter.
|
||||
*/
|
||||
TEST_F(PluginRegistryTest, ThirdPartyAdapterFlowsThroughCoreUnchanged)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
ASSERT_TRUE(registry.registerAdapter(std::make_shared<DummySourceAdapter>()));
|
||||
|
||||
MissionManager manager;
|
||||
EventProcessor processor(manager, registry);
|
||||
processor.start();
|
||||
|
||||
processor.submitRequest(
|
||||
MissionRequest::fromRaw(DummySourceAdapter::kSchema, "{\"job\":\"pick\"}"));
|
||||
|
||||
EXPECT_TRUE(mission_test::waitForState(manager, MissionState::QUEUED,
|
||||
std::chrono::milliseconds(500)));
|
||||
|
||||
// Payload không hợp lệ -> validate() chặn -> hàng đợi không đổi (A1).
|
||||
processor.submitRequest(MissionRequest::fromRaw(DummySourceAdapter::kSchema, ""));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(80));
|
||||
|
||||
EXPECT_EQ(manager.state(), MissionState::QUEUED);
|
||||
EXPECT_TRUE(manager.hasMission());
|
||||
|
||||
processor.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Plugin hỏng: sinh mission không goal và cũng không action.
|
||||
*
|
||||
* Core phải chặn, vì chặng như vậy không có việc gì để làm và sẽ không bao giờ báo kết quả về —
|
||||
* mission layer kẹt RUNNING vĩnh viễn.
|
||||
*/
|
||||
class EmptyMissionAdapter : public MissionSourceAdapter
|
||||
{
|
||||
public:
|
||||
static constexpr const char* kSchema = "test.empty_mission";
|
||||
|
||||
bool configure(const std::string&, robot::NodeHandle&) override { return true; }
|
||||
std::string schema() const override { return kSchema; }
|
||||
bool validate(const MissionRequest&, std::string&) const override { return true; }
|
||||
|
||||
ConversionResult convert(const MissionRequest&) override
|
||||
{
|
||||
auto mission = std::make_shared<Mission>();
|
||||
mission->has_goal = false; // và actions rỗng
|
||||
|
||||
ConversionResult result;
|
||||
result.missions.push_back(mission);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PluginRegistryTest, MissionWithoutGoalAndWithoutActionIsRejected)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
ASSERT_TRUE(registry.registerAdapter(std::make_shared<EmptyMissionAdapter>()));
|
||||
|
||||
MissionManager manager;
|
||||
EventProcessor processor(manager, registry);
|
||||
|
||||
processor.start();
|
||||
processor.submitRequest(MissionRequest::fromRaw(EmptyMissionAdapter::kSchema, "payload"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(80));
|
||||
processor.stop();
|
||||
|
||||
EXPECT_EQ(manager.state(), MissionState::IDLE);
|
||||
EXPECT_FALSE(manager.hasMission());
|
||||
}
|
||||
|
||||
TEST_F(PluginRegistryTest, UnknownSchemaIsIgnoredNotCrashing)
|
||||
{
|
||||
PluginRegistry registry;
|
||||
MissionManager manager;
|
||||
EventProcessor processor(manager, registry);
|
||||
|
||||
processor.start();
|
||||
processor.submitRequest(MissionRequest::fromRaw("nobody.handles.this", "payload"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(80));
|
||||
processor.stop();
|
||||
|
||||
EXPECT_EQ(manager.state(), MissionState::IDLE);
|
||||
EXPECT_FALSE(manager.hasMission());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Test tự trỏ vào cây config và thư mục .so của chính nó, để chạy được cả qua ctest lẫn khi gọi
|
||||
// thẳng binary (đúng cách test_costmap đang làm). overwrite = 0 nên biến môi trường do người
|
||||
// chạy đặt vẫn thắng.
|
||||
#ifdef MISSION_ADAPTERS_TEST_CONFIG_DIR
|
||||
setenv("PNKX_NAV_CORE_CONFIG_DIR", MISSION_ADAPTERS_TEST_CONFIG_DIR, 0);
|
||||
#endif
|
||||
#ifdef MISSION_ADAPTERS_TEST_LIBRARY_DIR
|
||||
setenv("PNKX_NAV_CORE_LIBRARY_PATH", MISSION_ADAPTERS_TEST_LIBRARY_DIR, 0);
|
||||
#endif
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Reference in New Issue
Block a user