changeset 815:28ff6cb5bf73

tests: add test_plugin_loader and broken_plugin, closes #932 @1h
author David Demelier <markand@malikania.fr>
date Fri, 16 Nov 2018 21:08:00 +0100
parents d0fc4f14678d
children 42992b30f928
files libirccd-daemon/irccd/daemon/plugin.hpp libirccd-daemon/irccd/daemon/plugin_service.cpp libirccd-test/CMakeLists.txt libirccd-test/irccd/test.hpp libirccd-test/irccd/test/broken_plugin.cpp libirccd-test/irccd/test/broken_plugin.hpp libirccd-test/irccd/test/cli_fixture.cpp libirccd-test/irccd/test/irccd_fixture.cpp libirccd-test/irccd/test/test_plugin_loader.cpp libirccd-test/irccd/test/test_plugin_loader.hpp tests/src/irccdctl/cli-plugin-load/main.cpp tests/src/irccdctl/cli-plugin-reload/main.cpp tests/src/irccdctl/cli-plugin-unload/main.cpp tests/src/libirccd-daemon/command-plugin-load/main.cpp tests/src/libirccd-daemon/command-plugin-reload/main.cpp tests/src/libirccd-daemon/command-plugin-unload/main.cpp
diffstat 16 files changed, 563 insertions(+), 198 deletions(-) [+]
line wrap: on
line diff
--- a/libirccd-daemon/irccd/daemon/plugin.hpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/libirccd-daemon/irccd/daemon/plugin.hpp	Fri Nov 16 21:08:00 2018 +0100
@@ -342,8 +342,8 @@
 	 * \param directories optional list of directories to search
 	 * \param extensions optional list of extensions
 	 */
-	plugin_loader(std::vector<std::string> directories,
-	              std::vector<std::string> extensions) noexcept;
+	plugin_loader(std::vector<std::string> directories = {},
+	              std::vector<std::string> extensions = {}) noexcept;
 
 	/**
 	 * Virtual destructor defaulted.
--- a/libirccd-daemon/irccd/daemon/plugin_service.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/libirccd-daemon/irccd/daemon/plugin_service.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -163,6 +163,10 @@
 auto plugin_service::find(std::string_view id) -> std::shared_ptr<plugin>
 {
 	for (const auto& loader : loaders_) {
+		/*
+		 * Protect loader->find function to continue searching other
+		 * appropriate plugins.
+		 */
 		try {
 			auto plugin = loader->find(id);
 
@@ -183,21 +187,28 @@
 
 	std::shared_ptr<plugin> plugin;
 
-	if (path.empty())
-		plugin = find(id);
-	else
-		plugin = open(id, std::move(path));
+	try {
+		if (path.empty())
+			plugin = find(id);
+		else
+			plugin = open(id, std::move(path));
+	} catch (...) {
+		throw plugin_error(plugin_error::exec_error, id);
+	}
 
 	if (!plugin)
 		throw plugin_error(plugin_error::not_found, id);
 
-	plugin->set_options(get_options(id));
-	plugin->set_formats(get_formats(id));
-	plugin->set_paths(get_paths(id));
+	try {
+		plugin->set_options(get_options(id));
+		plugin->set_formats(get_formats(id));
+		plugin->set_paths(get_paths(id));
+	} catch (...) {
+		throw plugin_error(plugin_error::exec_error, id);
+	}
 
 	exec(plugin, &plugin::handle_load, bot_);
 	add(plugin);
-
 	bot_.get_log().info(*plugin) << "loaded version " << plugin->get_version() << std::endl;
 }
 
--- a/libirccd-test/CMakeLists.txt	Fri Nov 16 13:18:28 2018 +0100
+++ b/libirccd-test/CMakeLists.txt	Fri Nov 16 21:08:00 2018 +0100
@@ -21,6 +21,8 @@
 set(
 	SOURCES
 	${libirccd-test_SOURCE_DIR}/irccd/test.hpp
+	${libirccd-test_SOURCE_DIR}/irccd/test/broken_plugin.cpp
+	${libirccd-test_SOURCE_DIR}/irccd/test/broken_plugin.hpp
 	${libirccd-test_SOURCE_DIR}/irccd/test/cli_fixture.cpp
 	${libirccd-test_SOURCE_DIR}/irccd/test/cli_fixture.hpp
 	${libirccd-test_SOURCE_DIR}/irccd/test/command_fixture.cpp
@@ -35,6 +37,8 @@
 	${libirccd-test_SOURCE_DIR}/irccd/test/mock_plugin.hpp
 	${libirccd-test_SOURCE_DIR}/irccd/test/mock_server.cpp
 	${libirccd-test_SOURCE_DIR}/irccd/test/mock_server.hpp
+	${libirccd-test_SOURCE_DIR}/irccd/test/test_plugin_loader.cpp
+	${libirccd-test_SOURCE_DIR}/irccd/test/test_plugin_loader.hpp
 )
 
 if (${IRCCD_HAVE_JS})
--- a/libirccd-test/irccd/test.hpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/libirccd-test/irccd/test.hpp	Fri Nov 16 21:08:00 2018 +0100
@@ -26,14 +26,16 @@
 
 #include "sysconfig.hpp"
 
+#include "test/broken_plugin.hpp"
+#include "test/cli_fixture.hpp"
+#include "test/command_fixture.hpp"
+#include "test/debug_server.hpp"
 #include "test/irccd_fixture.hpp"
+#include "test/js_fixture.hpp"
 #include "test/js_plugin_fixture.hpp"
 #include "test/mock.hpp"
+#include "test/mock_plugin.hpp"
 #include "test/mock_server.hpp"
-#include "test/mock_plugin.hpp"
-#include "test/cli_fixture.hpp"
-#include "test/js_fixture.hpp"
-#include "test/debug_server.hpp"
-#include "test/command_fixture.hpp"
+#include "test/test_plugin_loader.hpp"
 
 #endif // !IRCCD_TEST_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/test/broken_plugin.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -0,0 +1,187 @@
+/*
+ * broken_plugin.cpp -- broken plugin
+ *
+ * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <irccd/daemon/server.hpp>
+
+#include "broken_plugin.hpp"
+
+using irccd::daemon::connect_event;
+using irccd::daemon::disconnect_event;
+using irccd::daemon::invite_event;
+using irccd::daemon::bot;
+using irccd::daemon::join_event;
+using irccd::daemon::kick_event;
+using irccd::daemon::me_event;
+using irccd::daemon::message_event;
+using irccd::daemon::mode_event;
+using irccd::daemon::names_event;
+using irccd::daemon::nick_event;
+using irccd::daemon::notice_event;
+using irccd::daemon::part_event;
+using irccd::daemon::plugin;
+using irccd::daemon::topic_event;
+using irccd::daemon::whois_event;
+
+namespace irccd::test {
+
+auto broken_plugin::get_name() const noexcept -> std::string_view
+{
+	return "broken";
+}
+
+auto broken_plugin::get_author() const noexcept -> std::string_view
+{
+	return "David Demelier <markand@malikania.fr>";
+}
+
+auto broken_plugin::get_license() const noexcept -> std::string_view
+{
+	return "ISC";
+}
+
+auto broken_plugin::get_summary() const noexcept -> std::string_view
+{
+	return "broken plugin";
+}
+
+auto broken_plugin::get_version() const noexcept -> std::string_view
+{
+	return "1.0";
+}
+
+auto broken_plugin::get_options() const -> map
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::set_options(const map&)
+{
+	throw std::runtime_error("broken");
+}
+
+auto broken_plugin::get_formats() const -> map
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::set_formats(const map&)
+{
+	throw std::runtime_error("broken");
+}
+
+auto broken_plugin::get_paths() const -> map
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::set_paths(const map&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_command(bot&, const message_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_connect(bot&, const connect_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_disconnect(bot&, const disconnect_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_invite(bot&, const invite_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_join(bot&, const join_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_kick(bot&, const kick_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_load(bot&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_message(bot&, const message_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_me(bot&, const me_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_mode(bot&, const mode_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_names(bot&, const names_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_nick(bot&, const nick_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_notice(bot&, const notice_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_part(bot&, const part_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_reload(bot&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_topic(bot&, const topic_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_unload(bot&)
+{
+	throw std::runtime_error("broken");
+}
+
+void broken_plugin::handle_whois(bot&, const whois_event&)
+{
+	throw std::runtime_error("broken");
+}
+
+} // !irccd::test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/test/broken_plugin.hpp	Fri Nov 16 21:08:00 2018 +0100
@@ -0,0 +1,186 @@
+/*
+ * broken_plugin.hpp -- broken plugin
+ *
+ * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_TEST_BROKEN_PLUGIN_HPP
+#define IRCCD_TEST_BROKEN_PLUGIN_HPP
+
+/**
+ * \file broken_plugin.hpp
+ * \brief Broken plugin.
+ */
+
+#include <irccd/daemon/plugin.hpp>
+
+namespace irccd::test {
+
+/**
+ * \brief Broken plugin.
+ */
+class broken_plugin : public daemon::plugin {
+public:
+	using plugin::plugin;
+
+	/**
+	 * \copydoc daemon::plugin::get_name
+	 */
+	auto get_name() const noexcept -> std::string_view override;
+
+	/**
+	 * \copydoc daemon::plugin::get_author
+	 */
+	auto get_author() const noexcept -> std::string_view override;
+
+	/**
+	 * \copydoc daemon::plugin::get_license
+	 */
+	auto get_license() const noexcept -> std::string_view override;
+
+	/**
+	 * \copydoc daemon::plugin::get_summary
+	 */
+	auto get_summary() const noexcept -> std::string_view override;
+
+	/**
+	 * \copydoc daemon::plugin::get_version
+	 */
+	auto get_version() const noexcept -> std::string_view override;
+
+	/**
+	 * \copydoc daemon::plugin::get_options
+	 */
+	auto get_options() const -> map override;
+
+	/**
+	 * \copydoc daemon::plugin::set_options
+	 */
+	void set_options(const map& map) override;
+
+	/**
+	 * \copydoc daemon::plugin::get_formats
+	 */
+	auto get_formats() const -> map override;
+
+	/**
+	 * \copydoc daemon::plugin::set_formats
+	 */
+	void set_formats(const map& map) override;
+
+	/**
+	 * \copydoc daemon::plugin::get_paths
+	 */
+	auto get_paths() const -> map override;
+
+	/**
+	 * \copydoc daemon::plugin::set_paths
+	 */
+	void set_paths(const map& map) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_command
+	 */
+	void handle_command(daemon::bot& bot, const daemon::message_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_connect
+	 */
+	void handle_connect(daemon::bot& bot, const daemon::connect_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_disconnect
+	 */
+	void handle_disconnect(daemon::bot& bot, const daemon::disconnect_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_invite
+	 */
+	void handle_invite(daemon::bot& bot, const daemon::invite_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_join
+	 */
+	void handle_join(daemon::bot& bot, const daemon::join_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_kick
+	 */
+	void handle_kick(daemon::bot& bot, const daemon::kick_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_load
+	 */
+	void handle_load(daemon::bot& bot) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_message
+	 */
+	void handle_message(daemon::bot& bot, const daemon::message_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_me
+	 */
+	void handle_me(daemon::bot& bot, const daemon::me_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_mode
+	 */
+	void handle_mode(daemon::bot& bot, const daemon::mode_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_names
+	 */
+	void handle_names(daemon::bot& bot, const daemon::names_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_nick
+	 */
+	void handle_nick(daemon::bot& bot, const daemon::nick_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_notice
+	 */
+	void handle_notice(daemon::bot& bot, const daemon::notice_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_part
+	 */
+	void handle_part(daemon::bot& bot, const daemon::part_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_reload
+	 */
+	void handle_reload(daemon::bot& bot) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_topic
+	 */
+	void handle_topic(daemon::bot& bot, const daemon::topic_event& event) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_unload
+	 */
+	void handle_unload(daemon::bot& bot) override;
+
+	/**
+	 * \copydoc daemon::plugin::handle_whois
+	 */
+	void handle_whois(daemon::bot& bot, const daemon::whois_event& event) override;
+};
+
+} // !irccd::test
+
+#endif // !IRCCD_TEST_BROKEN_PLUGIN_HPP
--- a/libirccd-test/irccd/test/cli_fixture.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/libirccd-test/irccd/test/cli_fixture.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -29,6 +29,7 @@
 #include <irccd/daemon/transport_server.hpp>
 
 #include "cli_fixture.hpp"
+#include "test_plugin_loader.hpp"
 
 namespace proc = boost::process;
 
@@ -68,6 +69,7 @@
 
 	bot_.servers().add(server_);
 	bot_.transports().add(std::make_unique<transport_server>(std::move(acceptor)));
+	bot_.plugins().add_loader(std::make_unique<test_plugin_loader>());
 	server_->disconnect();
 	server_->clear();
 }
@@ -86,7 +88,6 @@
 	std::this_thread::sleep_for(std::chrono::milliseconds(250));
 }
 
-
 auto cli_fixture::exec(const std::vector<std::string>& args) -> result
 {
 	std::ostringstream oss;
--- a/libirccd-test/irccd/test/irccd_fixture.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/libirccd-test/irccd/test/irccd_fixture.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -17,14 +17,21 @@
  */
 
 #include <irccd/daemon/logger.hpp>
+#include <irccd/daemon/plugin_service.hpp>
 
 #include "irccd_fixture.hpp"
+#include "test_plugin_loader.hpp"
+
+using irccd::daemon::logger::silent_sink;
+
+using std::make_unique;
 
 namespace irccd::test {
 
 irccd_fixture::irccd_fixture()
 {
-	bot_.set_log(std::make_unique<daemon::logger::silent_sink>());
+	bot_.set_log(make_unique<silent_sink>());
+	bot_.plugins().add_loader(make_unique<test_plugin_loader>());
 }
 
 } // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/test/test_plugin_loader.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -0,0 +1,58 @@
+/*
+ * test_plugin_loader.cpp -- special plugin loader for unit tests
+ *
+ * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "broken_plugin.hpp"
+#include "mock_plugin.hpp"
+#include "test_plugin_loader.hpp"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::string;
+using std::string_view;
+
+using irccd::daemon::plugin;
+
+namespace irccd::test {
+
+namespace {
+
+auto create(string_view id) -> shared_ptr<plugin>
+{
+	string strid(id);
+
+	if (id == "broken")
+		return make_shared<broken_plugin>(strid);
+	if (id == "mock")
+		return make_shared<mock_plugin>(strid);
+
+	return nullptr;
+}
+
+} // !namespace
+
+auto test_plugin_loader::open(string_view id, string_view) -> shared_ptr<plugin>
+{
+	return create(id);
+}
+
+auto test_plugin_loader::find(string_view id) -> shared_ptr<plugin>
+{
+	return create(id);
+}
+
+} // !irccd::test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/test/test_plugin_loader.hpp	Fri Nov 16 21:08:00 2018 +0100
@@ -0,0 +1,57 @@
+/*
+ * test_plugin_loader.hpp -- special plugin loader for unit tests
+ *
+ * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_TEST_PLUGIN_LOADER_HPP
+#define IRCCD_TEST_PLUGIN_LOADER_HPP
+
+/**
+ * \file test_plugin_loader.hpp
+ * \brief Special plugin loader for unit tests.
+ */
+
+#include <irccd/daemon/plugin.hpp>
+
+namespace irccd::test {
+
+/**
+ * \brief Special plugin loader for unit tests.
+ *
+ * This class reimplements the functions find and open to return special plugin
+ * objects.
+ *
+ * The following names are supported:
+ *
+ * - broken: will instanciate a broken_plugin object
+ * - mock: will instanciate a mock_plugin object
+ */
+class test_plugin_loader : public daemon::plugin_loader {
+public:
+	/**
+	 * \copydoc daemon::plugin_loader::open
+	 */
+	auto open(std::string_view id, std::string_view file) -> std::shared_ptr<daemon::plugin> override;
+
+	/**
+	 * \copydoc daemon::plugin_loader::find
+	 */
+	auto find(std::string_view id) -> std::shared_ptr<daemon::plugin> override;
+};
+
+} // !irccd::test
+
+#endif // !IRCCD_TEST_PLUGIN_LOADER_HPP
--- a/tests/src/irccdctl/cli-plugin-load/main.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/tests/src/irccdctl/cli-plugin-load/main.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -32,24 +32,6 @@
 
 namespace {
 
-class custom_plugin_loader : public plugin_loader {
-public:
-	custom_plugin_loader()
-		: plugin_loader({}, { "none" })
-	{
-	}
-
-	auto find(std::string_view id) -> std::shared_ptr<plugin> override
-	{
-		return std::make_unique<mock_plugin>(std::string(id));
-	}
-
-	auto open(std::string_view id, std::string_view) -> std::shared_ptr<plugin> override
-	{
-		return std::make_unique<mock_plugin>(std::string(id));
-	}
-};
-
 class plugin_list_fixture : public cli_fixture {
 public:
 	plugin_list_fixture()
@@ -66,12 +48,11 @@
 {
 	bot_.plugins().add(std::make_unique<mock_plugin>("p1"));
 	bot_.plugins().add(std::make_unique<mock_plugin>("p2"));
-	bot_.plugins().add_loader(std::make_unique<custom_plugin_loader>());
 	start();
 
 	// Load a plugin first.
 	{
-		const auto [code, out, err] = exec({ "plugin-load", "test" });
+		const auto [code, out, err] = exec({ "plugin-load", "mock" });
 
 		BOOST_TEST(!code);
 		BOOST_TEST(out.size() == 0U);
@@ -87,10 +68,13 @@
 		BOOST_TEST(err.size() == 0U);
 		BOOST_TEST(out[0] == "p1");
 		BOOST_TEST(out[1] == "p2");
-		BOOST_TEST(out[2] == "test");
+		BOOST_TEST(out[2] == "mock");
 	}
 }
 
+// TODO: not_found
+// TODO: exec_error
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // !irccd
--- a/tests/src/irccdctl/cli-plugin-reload/main.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/tests/src/irccdctl/cli-plugin-reload/main.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -20,41 +20,26 @@
 #include <boost/test/unit_test.hpp>
 
 #include <irccd/test/cli_fixture.hpp>
-#include <irccd/test/mock.hpp>
-
-using irccd::test::cli_fixture;
-using irccd::test::mock;
+#include <irccd/test/mock_plugin.hpp>
 
 using irccd::daemon::bot;
 using irccd::daemon::plugin;
 
+using irccd::test::cli_fixture;
+using irccd::test::mock_plugin;
+
 namespace irccd {
 
 namespace {
 
-class reloadable_plugin : public mock, public plugin {
-public:
-	reloadable_plugin()
-		: plugin("test")
-	{
-	}
+class plugin_reload_fixture : public cli_fixture {
+protected:
+	std::shared_ptr<mock_plugin> plugin_{new mock_plugin("test")};
 
-	auto get_name() const noexcept -> std::string_view override
-	{
-		return "reload";
-	}
-
-	void handle_reload(bot&) override
-	{
-		push("handle_reload");
-	}
-};
-
-class plugin_reload_fixture : public cli_fixture {
-public:
 	plugin_reload_fixture()
 		: cli_fixture(IRCCDCTL_EXECUTABLE)
 	{
+		bot_.plugins().add(plugin_);
 	}
 };
 
@@ -62,9 +47,6 @@
 
 BOOST_AUTO_TEST_CASE(simple)
 {
-	const auto plugin = std::make_shared<reloadable_plugin>();
-
-	bot_.plugins().add(plugin);
 	start();
 
 	const auto [code, out, err] = exec({ "plugin-reload", "test" });
@@ -72,7 +54,7 @@
 	BOOST_TEST(!code);
 	BOOST_TEST(out.size() == 0U);
 	BOOST_TEST(err.size() == 0U);
-	BOOST_TEST(plugin->find("handle_reload").size() == 1U);
+	BOOST_TEST(plugin_->find("handle_reload").size() == 1U);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/irccdctl/cli-plugin-unload/main.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/tests/src/irccdctl/cli-plugin-unload/main.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -20,41 +20,26 @@
 #include <boost/test/unit_test.hpp>
 
 #include <irccd/test/cli_fixture.hpp>
-#include <irccd/test/mock.hpp>
-
-using irccd::test::cli_fixture;
-using irccd::test::mock;
+#include <irccd/test/mock_plugin.hpp>
 
 using irccd::daemon::bot;
 using irccd::daemon::plugin;
 
+using irccd::test::cli_fixture;
+using irccd::test::mock_plugin;
+
 namespace irccd {
 
 namespace {
 
-class unloadable_plugin : public mock, public plugin {
-public:
-	unloadable_plugin()
-		: plugin("test")
-	{
-	}
+class plugin_unload_fixture : public cli_fixture {
+protected:
+	std::shared_ptr<mock_plugin> plugin_{new mock_plugin("test")};
 
-	auto get_name() const noexcept -> std::string_view override
-	{
-		return "unload";
-	}
-
-	void handle_unload(bot&) override
-	{
-		push("handle_unload");
-	}
-};
-
-class plugin_unload_fixture : public cli_fixture {
-public:
 	plugin_unload_fixture()
 		: cli_fixture(IRCCDCTL_EXECUTABLE)
 	{
+		bot_.plugins().add(plugin_);
 	}
 };
 
@@ -62,9 +47,6 @@
 
 BOOST_AUTO_TEST_CASE(simple)
 {
-	const auto plugin = std::make_shared<unloadable_plugin>();
-
-	bot_.plugins().add(plugin);
 	start();
 
 	const auto [code, out, err] = exec({ "plugin-unload", "test" });
@@ -72,7 +54,7 @@
 	BOOST_TEST(!code);
 	BOOST_TEST(out.size() == 0U);
 	BOOST_TEST(err.size() == 0U);
-	BOOST_TEST(plugin->find("handle_unload").size() == 1U);
+	BOOST_TEST(plugin_->find("handle_unload").size() == 1U);
 	BOOST_TEST(!bot_.plugins().has("p"));
 }
 
--- a/tests/src/libirccd-daemon/command-plugin-load/main.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/tests/src/libirccd-daemon/command-plugin-load/main.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -33,72 +33,10 @@
 
 namespace {
 
-class broken : public plugin {
-public:
-	broken()
-		: plugin("broken")
-	{
-	}
-
-	auto get_name() const noexcept -> std::string_view override
-	{
-		return "broken";
-	}
-
-	void handle_load(bot&) override
-	{
-		throw std::runtime_error("broken");
-	}
-};
-
-class broken_loader : public plugin_loader {
-public:
-	broken_loader()
-		: plugin_loader({}, { ".none" })
-	{
-	}
-
-	auto open(std::string_view, std::string_view) -> std::shared_ptr<plugin> override
-	{
-		return nullptr;
-	}
-
-	auto find(std::string_view id) noexcept -> std::shared_ptr<plugin> override
-	{
-		if (id == "broken")
-			return std::make_unique<broken>();
-
-		return nullptr;
-	}
-};
-
-class sample_loader : public plugin_loader {
-public:
-	sample_loader()
-		: plugin_loader({}, { ".none" })
-	{
-	}
-
-	auto open(std::string_view, std::string_view) -> std::shared_ptr<plugin> override
-	{
-		return nullptr;
-	}
-
-	auto find(std::string_view id) noexcept -> std::shared_ptr<plugin> override
-	{
-		if (id == "test")
-			return std::make_unique<mock_plugin>("test");
-
-		return nullptr;
-	}
-};
-
 class plugin_load_fixture : public command_fixture {
 public:
 	plugin_load_fixture()
 	{
-		bot_.plugins().add_loader(std::make_unique<sample_loader>());
-		bot_.plugins().add_loader(std::make_unique<broken_loader>());
 		bot_.plugins().clear();
 		bot_.plugins().add(std::make_unique<mock_plugin>("already"));
 	}
@@ -112,11 +50,11 @@
 {
 	const auto [json, code] = request({
 		{ "command",    "plugin-load"   },
-		{ "plugin",     "test"          }
+		{ "plugin",     "mock"          }
 	});
 
 	BOOST_TEST(!code);
-	BOOST_TEST(bot_.plugins().has("test"));
+	BOOST_TEST(bot_.plugins().has("mock"));
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd-daemon/command-plugin-reload/main.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/tests/src/libirccd-daemon/command-plugin-reload/main.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -19,8 +19,10 @@
 #define BOOST_TEST_MODULE "plugin-reload"
 #include <boost/test/unit_test.hpp>
 
+#include <irccd/test/broken_plugin.hpp>
 #include <irccd/test/command_fixture.hpp>
 
+using irccd::test::broken_plugin;
 using irccd::test::command_fixture;
 using irccd::test::mock_plugin;
 
@@ -33,34 +35,15 @@
 
 namespace {
 
-class broken_plugin : public plugin {
-public:
-	broken_plugin()
-		: plugin("broken")
-	{
-	}
-
-	auto get_name() const noexcept -> std::string_view override
-	{
-		return "broken";
-	}
-
-	void handle_reload(bot&) override
-	{
-		throw std::runtime_error("broken");
-	}
-};
-
 class plugin_reload_fixture : public command_fixture {
 protected:
-	std::shared_ptr<mock_plugin> plugin_;
+	std::shared_ptr<mock_plugin> plugin_{new mock_plugin("test")};
 
 	plugin_reload_fixture()
-		: plugin_(std::make_shared<mock_plugin>("test"))
 	{
 		bot_.plugins().clear();
 		bot_.plugins().add(plugin_);
-		bot_.plugins().add(std::make_unique<broken_plugin>());
+		bot_.plugins().add(std::make_unique<broken_plugin>("broken"));
 	}
 };
 
--- a/tests/src/libirccd-daemon/command-plugin-unload/main.cpp	Fri Nov 16 13:18:28 2018 +0100
+++ b/tests/src/libirccd-daemon/command-plugin-unload/main.cpp	Fri Nov 16 21:08:00 2018 +0100
@@ -19,8 +19,10 @@
 #define BOOST_TEST_MODULE "plugin-unload"
 #include <boost/test/unit_test.hpp>
 
+#include <irccd/test/broken_plugin.hpp>
 #include <irccd/test/command_fixture.hpp>
 
+using irccd::test::broken_plugin;
 using irccd::test::command_fixture;
 using irccd::test::mock_plugin;
 
@@ -33,34 +35,15 @@
 
 namespace {
 
-class broken_plugin : public plugin {
-public:
-	broken_plugin()
-		: plugin("broken")
-	{
-	}
-
-	auto get_name() const noexcept -> std::string_view override
-	{
-		return "broken";
-	}
-
-	void handle_unload(bot&) override
-	{
-		throw std::runtime_error("broken");
-	}
-};
-
 class plugin_unload_fixture : public command_fixture {
 protected:
-	std::shared_ptr<mock_plugin> plugin_;
+	std::shared_ptr<mock_plugin> plugin_{new mock_plugin("test")};
 
 	plugin_unload_fixture()
-		: plugin_(std::make_shared<mock_plugin>("test"))
 	{
 		bot_.plugins().clear();
 		bot_.plugins().add(plugin_);
-		bot_.plugins().add(std::make_unique<broken_plugin>());
+		bot_.plugins().add(std::make_unique<broken_plugin>("broken"));
 	}
 };