optimal & fix file cmake

This commit is contained in:
2026-08-03 22:40:26 +07:00
parent 33ee9b7f31
commit 2223453639
120 changed files with 12204 additions and 1886 deletions

View 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();
}