#include "lldb/Core/PluginManager.h" #include "gtest/gtest.h" using namespace lldb; using namespace lldb_private; // Mock system runtime plugin create functions. // Make them all return different values to avoid the ICF optimization // from combining them into the same function. The values returned // are not valid SystemRuntime pointers, but they are unique and // sufficient for testing. SystemRuntime *CreateSystemRuntimePluginA(Process *process) { return (SystemRuntime *)0x1; } SystemRuntime *CreateSystemRuntimePluginB(Process *process) { return (SystemRuntime *)0x2; } SystemRuntime *CreateSystemRuntimePluginC(Process *process) { return (SystemRuntime *)0x3; } // Test class for testing the PluginManager. // The PluginManager modifies global state when registering new plugins. This // class is intended to undo those modifications in the destructor to give each // test a clean slate with no registered plugins at the start of a test. class PluginManagerTest : public testing::Test { public: // Remove any pre-registered plugins so we have a known starting point. static void SetUpTestSuite() { RemoveAllRegisteredSystemRuntimePlugins(); } // Add mock system runtime plugins for testing. void RegisterMockSystemRuntimePlugins() { // Make sure the create functions all have different addresses. ASSERT_NE(CreateSystemRuntimePluginA, CreateSystemRuntimePluginB); ASSERT_NE(CreateSystemRuntimePluginB, CreateSystemRuntimePluginC); ASSERT_TRUE(PluginManager::RegisterPlugin("a", "test instance A", CreateSystemRuntimePluginA)); ASSERT_TRUE(PluginManager::RegisterPlugin("b", "test instance B", CreateSystemRuntimePluginB)); ASSERT_TRUE(PluginManager::RegisterPlugin("c", "test instance C", CreateSystemRuntimePluginC)); } // Remove any plugins added during the tests. virtual ~PluginManagerTest() override { RemoveAllRegisteredSystemRuntimePlugins(); } protected: std::vector m_system_runtime_plugins; static void RemoveAllRegisteredSystemRuntimePlugins() { // Enable all currently registered plugins so we can get a handle to // their create callbacks in the loop below. Only enabled plugins // are returned from the PluginManager Get*CreateCallbackAtIndex apis. for (const RegisteredPluginInfo &PluginInfo : PluginManager::GetSystemRuntimePluginInfo()) { PluginManager::SetSystemRuntimePluginEnabled(PluginInfo.name, true); } // Get a handle to the create call backs for all the registered plugins. std::vector registered_plugin_callbacks; SystemRuntimeCreateInstance create_callback = nullptr; for (uint32_t idx = 0; (create_callback = PluginManager::GetSystemRuntimeCreateCallbackAtIndex(idx)) != nullptr; ++idx) { registered_plugin_callbacks.push_back((create_callback)); } // Remove all currently registered plugins. for (SystemRuntimeCreateInstance create_callback : registered_plugin_callbacks) { PluginManager::UnregisterPlugin(create_callback); } } }; // Test basic register functionality. TEST_F(PluginManagerTest, RegisterSystemRuntimePlugin) { RegisterMockSystemRuntimePlugins(); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginB); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), CreateSystemRuntimePluginC); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(3), nullptr); } // Test basic un-register functionality. TEST_F(PluginManagerTest, UnRegisterSystemRuntimePlugin) { RegisterMockSystemRuntimePlugins(); ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB)); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginC); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), nullptr); } // Test registered plugin info functionality. TEST_F(PluginManagerTest, SystemRuntimePluginInfo) { RegisterMockSystemRuntimePlugins(); std::vector plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(plugin_info.size(), 3u); ASSERT_EQ(plugin_info[0].name, "a"); ASSERT_EQ(plugin_info[0].description, "test instance A"); ASSERT_EQ(plugin_info[0].enabled, true); ASSERT_EQ(plugin_info[1].name, "b"); ASSERT_EQ(plugin_info[1].description, "test instance B"); ASSERT_EQ(plugin_info[1].enabled, true); ASSERT_EQ(plugin_info[2].name, "c"); ASSERT_EQ(plugin_info[2].description, "test instance C"); ASSERT_EQ(plugin_info[2].enabled, true); } // Test basic un-register functionality. TEST_F(PluginManagerTest, UnRegisterSystemRuntimePluginInfo) { RegisterMockSystemRuntimePlugins(); // Initial plugin info has all three registered plugins. std::vector plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(plugin_info.size(), 3u); ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB)); // After un-registering a plugin it should be removed from plugin info. plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(plugin_info.size(), 2u); ASSERT_EQ(plugin_info[0].name, "a"); ASSERT_EQ(plugin_info[0].enabled, true); ASSERT_EQ(plugin_info[1].name, "c"); ASSERT_EQ(plugin_info[1].enabled, true); } // Test plugin disable functionality. TEST_F(PluginManagerTest, SystemRuntimePluginDisable) { RegisterMockSystemRuntimePlugins(); // Disable plugin should succeed. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false)); // Disabling a plugin does not remove it from plugin info. std::vector plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(plugin_info.size(), 3u); ASSERT_EQ(plugin_info[0].name, "a"); ASSERT_EQ(plugin_info[0].enabled, true); ASSERT_EQ(plugin_info[1].name, "b"); ASSERT_EQ(plugin_info[1].enabled, false); ASSERT_EQ(plugin_info[2].name, "c"); ASSERT_EQ(plugin_info[2].enabled, true); // Disabling a plugin does remove it from available plugins. ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginC); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), nullptr); } // Test plugin disable and enable functionality. TEST_F(PluginManagerTest, SystemRuntimePluginDisableThenEnable) { RegisterMockSystemRuntimePlugins(); // Initially plugin b is available in slot 1. ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginB); // Disabling it will remove it from available plugins. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false)); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginC); // We can re-enable the plugin later and it should go back to the original // slot. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", true)); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginB); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), CreateSystemRuntimePluginC); // And show up in the plugin info correctly. std::vector plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(plugin_info.size(), 3u); ASSERT_EQ(plugin_info[0].name, "a"); ASSERT_EQ(plugin_info[0].enabled, true); ASSERT_EQ(plugin_info[1].name, "b"); ASSERT_EQ(plugin_info[1].enabled, true); ASSERT_EQ(plugin_info[2].name, "c"); ASSERT_EQ(plugin_info[2].enabled, true); } // Test calling disable on an already disabled plugin is ok. TEST_F(PluginManagerTest, SystemRuntimePluginDisableDisabled) { RegisterMockSystemRuntimePlugins(); // Initial call to disable the plugin should succeed. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false)); // The second call should also succeed because the plugin is already disabled. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false)); // The call to re-enable the plugin should succeed. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", true)); // The second call should also succeed since the plugin is already enabled. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", true)); } // Test calling disable on an already disabled plugin is ok. TEST_F(PluginManagerTest, SystemRuntimePluginDisableNonExistent) { RegisterMockSystemRuntimePlugins(); // Both enable and disable should return false for a non-existent plugin. ASSERT_FALSE( PluginManager::SetSystemRuntimePluginEnabled("does_not_exist", true)); ASSERT_FALSE( PluginManager::SetSystemRuntimePluginEnabled("does_not_exist", false)); } // Test disabling all plugins and then re-enabling them in a different // order will restore the original plugin order. TEST_F(PluginManagerTest, SystemRuntimePluginDisableAll) { RegisterMockSystemRuntimePlugins(); // Validate initial state of registered plugins. ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginB); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), CreateSystemRuntimePluginC); // Disable all the active plugins. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("a", false)); ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false)); ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("c", false)); // Should have no active plugins. ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), nullptr); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), nullptr); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), nullptr); // And show up in the plugin info correctly. std::vector plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(plugin_info.size(), 3u); ASSERT_EQ(plugin_info[0].name, "a"); ASSERT_EQ(plugin_info[0].enabled, false); ASSERT_EQ(plugin_info[1].name, "b"); ASSERT_EQ(plugin_info[1].enabled, false); ASSERT_EQ(plugin_info[2].name, "c"); ASSERT_EQ(plugin_info[2].enabled, false); // Enable plugins in reverse order and validate expected indicies. // They should show up in the original plugin order. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("c", true)); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginC); ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("a", true)); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginC); ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", true)); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginB); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), CreateSystemRuntimePluginC); } // Test un-registering a disabled plugin works. TEST_F(PluginManagerTest, UnRegisterDisabledSystemRuntimePlugin) { RegisterMockSystemRuntimePlugins(); // Initial plugin info has all three registered plugins. std::vector plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(plugin_info.size(), 3u); // First disable a plugin, then unregister it. Both should succeed. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false)); ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB)); // After un-registering a plugin it should be removed from plugin info. plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(plugin_info.size(), 2u); ASSERT_EQ(plugin_info[0].name, "a"); ASSERT_EQ(plugin_info[0].enabled, true); ASSERT_EQ(plugin_info[1].name, "c"); ASSERT_EQ(plugin_info[1].enabled, true); } // Test un-registering and then re-registering a plugin will change the order of // loaded plugins. TEST_F(PluginManagerTest, UnRegisterSystemRuntimePluginChangesOrder) { RegisterMockSystemRuntimePlugins(); std::vector plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginB); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), CreateSystemRuntimePluginC); ASSERT_EQ(plugin_info.size(), 3u); ASSERT_EQ(plugin_info[0].name, "a"); ASSERT_EQ(plugin_info[1].name, "b"); ASSERT_EQ(plugin_info[2].name, "c"); // Unregister and then registering a plugin puts it at the end of the order // list. ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB)); ASSERT_TRUE(PluginManager::RegisterPlugin("b", "New test instance B", CreateSystemRuntimePluginB)); // Check the callback indices match as expected. plugin_info = PluginManager::GetSystemRuntimePluginInfo(); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginC); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), CreateSystemRuntimePluginB); // And plugin info should match as well. ASSERT_EQ(plugin_info.size(), 3u); ASSERT_EQ(plugin_info[0].name, "a"); ASSERT_EQ(plugin_info[1].name, "c"); ASSERT_EQ(plugin_info[2].name, "b"); ASSERT_EQ(plugin_info[2].description, "New test instance B"); // Disabling and re-enabling the "c" plugin should slot it back // into the middle of the order. Originally it was last, but after // un-registering and re-registering "b" it should now stay in // the middle of the order. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("c", false)); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginB); // And re-enabling ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("c", true)); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), CreateSystemRuntimePluginA); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), CreateSystemRuntimePluginC); ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), CreateSystemRuntimePluginB); } TEST_F(PluginManagerTest, MatchPluginName) { PluginNamespace Foo{"foo", nullptr, nullptr}; RegisteredPluginInfo Bar{"bar", "bar plugin ", true}; RegisteredPluginInfo Baz{"baz", "baz plugin ", true}; // Empty pattern matches everything. ASSERT_TRUE(PluginManager::MatchPluginName("", Foo, Bar)); // Plugin namespace matches all plugins in that namespace. ASSERT_TRUE(PluginManager::MatchPluginName("foo", Foo, Bar)); ASSERT_TRUE(PluginManager::MatchPluginName("foo", Foo, Baz)); // Fully qualified plugin name matches only that plugin. ASSERT_TRUE(PluginManager::MatchPluginName("foo.bar", Foo, Bar)); ASSERT_FALSE(PluginManager::MatchPluginName("foo.baz", Foo, Bar)); // Prefix match should not match. ASSERT_FALSE(PluginManager::MatchPluginName("f", Foo, Bar)); ASSERT_FALSE(PluginManager::MatchPluginName("foo.", Foo, Bar)); ASSERT_FALSE(PluginManager::MatchPluginName("foo.ba", Foo, Bar)); } TEST_F(PluginManagerTest, JsonFormat) { RegisterMockSystemRuntimePlugins(); // We expect the following JSON output: // { // "system-runtime": [ // { // "enabled": true, // "name": "a" // }, // { // "enabled": true, // "name": "b" // }, // { // "enabled": true, // "name": "c" // } // ] // } llvm::json::Object obj = PluginManager::GetJSON(); // We should have a "system-runtime" array in the top-level object. llvm::json::Array *maybe_array = obj.getArray("system-runtime"); ASSERT_TRUE(maybe_array != nullptr); auto &array = *maybe_array; ASSERT_EQ(array.size(), 3u); // Check plugin "a" info. ASSERT_TRUE(array[0].getAsObject() != nullptr); ASSERT_TRUE(array[0].getAsObject()->getString("name") == "a"); ASSERT_TRUE(array[0].getAsObject()->getBoolean("enabled") == true); // Check plugin "b" info. ASSERT_TRUE(array[1].getAsObject() != nullptr); ASSERT_TRUE(array[1].getAsObject()->getString("name") == "b"); ASSERT_TRUE(array[1].getAsObject()->getBoolean("enabled") == true); // Check plugin "c" info. ASSERT_TRUE(array[2].getAsObject() != nullptr); ASSERT_TRUE(array[2].getAsObject()->getString("name") == "c"); ASSERT_TRUE(array[2].getAsObject()->getBoolean("enabled") == true); // Disabling a plugin should be reflected in the JSON output. ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false)); array = *PluginManager::GetJSON().getArray("system-runtime"); ASSERT_TRUE(array[0].getAsObject()->getBoolean("enabled") == true); ASSERT_TRUE(array[1].getAsObject()->getBoolean("enabled") == false); ASSERT_TRUE(array[2].getAsObject()->getBoolean("enabled") == true); // Un-registering a plugin should be reflected in the JSON output. ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB)); array = *PluginManager::GetJSON().getArray("system-runtime"); ASSERT_EQ(array.size(), 2u); ASSERT_TRUE(array[0].getAsObject()->getString("name") == "a"); ASSERT_TRUE(array[1].getAsObject()->getString("name") == "c"); // Filtering the JSON output should only include the matching plugins. array = *PluginManager::GetJSON("system-runtime.c").getArray("system-runtime"); ASSERT_EQ(array.size(), 1u); ASSERT_TRUE(array[0].getAsObject()->getString("name") == "c"); // Empty JSON output is allowed if there are no matching plugins. obj = PluginManager::GetJSON("non-existent-plugin"); ASSERT_TRUE(obj.empty()); }