changeset 555:9b6b0d7d89c6

Tests: convert cmd-plugin-*, #593
author David Demelier <markand@malikania.fr>
date Fri, 24 Nov 2017 21:04:32 +0100
parents 6ff2172d2239
children c9b703f923d0
files libirccd-test/CMakeLists.txt libirccd-test/irccd/command_test.hpp libirccd/irccd/config.cpp libirccd/irccd/transport_server.cpp libirccd/irccd/transport_server.hpp libirccd/irccd/transport_service.hpp libirccdctl/irccd/ctl/controller.hpp tests/CMakeLists.txt tests/cmd-plugin-config/main.cpp tests/cmd-plugin-info/main.cpp tests/cmd-plugin-list/main.cpp tests/cmd-plugin-load/main.cpp tests/cmd-plugin-reload/main.cpp tests/cmd-plugin-unload/main.cpp
diffstat 14 files changed, 394 insertions(+), 347 deletions(-) [+]
line wrap: on
line diff
--- a/libirccd-test/CMakeLists.txt	Fri Nov 24 20:30:42 2017 +0100
+++ b/libirccd-test/CMakeLists.txt	Fri Nov 24 21:04:32 2017 +0100
@@ -21,6 +21,7 @@
 irccd_define_library(
     TARGET libirccd-test
     SOURCES
+        ${libirccd-test_SOURCE_DIR}/irccd/command_test.hpp
         ${libirccd-test_SOURCE_DIR}/irccd/journal_server.cpp
         ${libirccd-test_SOURCE_DIR}/irccd/journal_server.hpp
         $<$<BOOL:${HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/plugin_test.cpp>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/command_test.hpp	Fri Nov 24 21:04:32 2017 +0100
@@ -0,0 +1,102 @@
+/*
+ * command_test.hpp -- test fixture helper for transport commands
+ *
+ * Copyright (c) 2013-2017 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_COMMAND_TEST_HPP
+#define IRCCD_TEST_COMMAND_TEST_HPP
+
+#include <memory>
+
+#include <irccd/irccd.hpp>
+#include <irccd/command_service.hpp>
+#include <irccd/transport_service.hpp>
+
+#include <irccd/ctl/ip_connection.hpp>
+#include <irccd/ctl/controller.hpp>
+
+namespace irccd {
+
+template <typename Command>
+class command_test {
+protected:
+    boost::asio::io_service service_;
+    boost::asio::deadline_timer timer_;
+
+    // daemon stuff.
+    std::unique_ptr<irccd> daemon_;
+
+    // controller stuff.
+    std::unique_ptr<ctl::connection> conn_;
+    std::unique_ptr<ctl::controller> ctl_;
+
+    command_test();
+
+    template <typename Condition>
+    void wait_for(Condition&& cond)
+    {
+        while (!cond())
+            service_.poll();
+    }
+};
+
+template <typename Command>
+command_test<Command>::command_test()
+    : timer_(service_)
+    , daemon_(std::make_unique<irccd>(service_))
+{
+    using boost::asio::ip::tcp;
+
+    // Bind to a random port.
+    tcp::endpoint ep(tcp::v4(), 0);
+    tcp::acceptor acc(service_, ep);
+
+    // Connect to the local bound port.
+    conn_ = std::make_unique<ctl::ip_connection>(service_, "127.0.0.1", acc.local_endpoint().port());
+    ctl_ = std::make_unique<ctl::controller>(*conn_);
+
+    // Add the server and the command.
+    daemon_->commands().add(std::make_unique<Command>());
+    daemon_->transports().add(std::make_unique<ip_transport_server>(std::move(acc)));
+
+    timer_.expires_from_now(boost::posix_time::seconds(10));
+    timer_.async_wait([] (auto code) {
+        if (!code)
+            throw make_error_code(boost::system::errc::timed_out);
+    });
+
+    bool connected = false;
+
+    ctl_->connect([&] (auto code, auto) {
+        if (code)
+            throw code;
+
+        connected = true;
+        timer_.cancel();
+    });
+
+    while (!connected)
+        service_.poll();
+
+    service_.reset();
+
+    if (!connected)
+        throw std::runtime_error("unable to connect");
+}
+
+} // !irccd
+
+#endif // !IRCCD_TEST_COMMAND_TEST_HPP
--- a/libirccd/irccd/config.cpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/libirccd/irccd/config.cpp	Fri Nov 24 21:04:32 2017 +0100
@@ -195,7 +195,7 @@
     boost::asio::ip::tcp::acceptor acceptor(service, endpoint, true);
 
     if (pkey.empty())
-        return std::make_unique<tcp_transport_server>(std::move(acceptor));
+        return std::make_unique<ip_transport_server>(std::move(acceptor));
 
 #if defined(HAVE_SSL)
     boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
--- a/libirccd/irccd/transport_server.cpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/libirccd/irccd/transport_server.cpp	Fri Nov 24 21:04:32 2017 +0100
@@ -107,7 +107,7 @@
 }
 
 tls_transport_server::tls_transport_server(acceptor_t acceptor, context_t context)
-    : tcp_transport_server(std::move(acceptor))
+    : ip_transport_server(std::move(acceptor))
     , context_(std::move(context))
 {
 }
--- a/libirccd/irccd/transport_server.hpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/libirccd/irccd/transport_server.hpp	Fri Nov 24 21:04:32 2017 +0100
@@ -199,7 +199,7 @@
 /**
  * Convenient type for IP/TCP
  */
-using tcp_transport_server = basic_transport_server<boost::asio::ip::tcp>;
+using ip_transport_server = basic_transport_server<boost::asio::ip::tcp>;
 
 #if !defined(_WIN32)
 
@@ -215,7 +215,7 @@
 /**
  * \brief Secure layer implementation.
  */
-class tls_transport_server : public tcp_transport_server {
+class tls_transport_server : public ip_transport_server {
 private:
     using context_t = boost::asio::ssl::context;
     using client_t = basic_transport_client<boost::asio::ssl::stream<socket_t>>;
--- a/libirccd/irccd/transport_service.hpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/libirccd/irccd/transport_service.hpp	Fri Nov 24 21:04:32 2017 +0100
@@ -24,10 +24,10 @@
 
 #include <json.hpp>
 
-namespace irccd {
+#include "transport_client.hpp"
+#include "transport_server.hpp"
 
-class transport_client;
-class transport_server;
+namespace irccd {
 
 /**
  * \brief manage transport servers and clients.
--- a/libirccdctl/irccd/ctl/controller.hpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/libirccdctl/irccd/ctl/controller.hpp	Fri Nov 24 21:04:32 2017 +0100
@@ -137,7 +137,7 @@
      * \param message the JSON message
      * \param handler the optional completion handler
      */
-    void send(nlohmann::json message, network_send_handler handler);
+    void send(nlohmann::json message, network_send_handler handler = nullptr);
 };
 
 } // !ctl
--- a/tests/CMakeLists.txt	Fri Nov 24 20:30:42 2017 +0100
+++ b/tests/CMakeLists.txt	Fri Nov 24 21:04:32 2017 +0100
@@ -20,12 +20,12 @@
 project(tests)
 
 if (WITH_TESTS)
-#    add_subdirectory(cmd-plugin-config)
-#    add_subdirectory(cmd-plugin-info)
-#    add_subdirectory(cmd-plugin-list)
-#    add_subdirectory(cmd-plugin-load)
-#    add_subdirectory(cmd-plugin-reload)
-#    add_subdirectory(cmd-plugin-unload)
+    add_subdirectory(cmd-plugin-config)
+    add_subdirectory(cmd-plugin-info)
+    add_subdirectory(cmd-plugin-list)
+    add_subdirectory(cmd-plugin-load)
+    add_subdirectory(cmd-plugin-reload)
+    add_subdirectory(cmd-plugin-unload)
 #    add_subdirectory(cmd-rule-add)
 #    add_subdirectory(cmd-rule-edit)
 #    add_subdirectory(cmd-rule-info)
--- a/tests/cmd-plugin-config/main.cpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/tests/cmd-plugin-config/main.cpp	Fri Nov 24 21:04:32 2017 +0100
@@ -16,136 +16,119 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
+#define BOOST_TEST_MODULE "plugin-config"
+#include <boost/test/unit_test.hpp>
 
-using namespace irccd;
+#include <irccd/command.hpp>
+#include <irccd/plugin_service.hpp>
+
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-struct CustomPlugin : public plugin {
-    plugin_config m_config;
+class custom_plugin : public plugin {
+public:
+    plugin_config config_;
 
-    CustomPlugin(std::string name = "test")
+    custom_plugin(std::string name = "test")
         : plugin(std::move(name), "")
     {
     }
 
     plugin_config config() override
     {
-        return m_config;
+        return config_;
     }
 
     void set_config(plugin_config config) override
     {
-        m_config = std::move(config);
-    }
-};
-
-class PluginConfigCommandTest : public CommandTester {
-public:
-    PluginConfigCommandTest()
-        : CommandTester(std::make_unique<plugin_config_command>())
-    {
+        config_ = std::move(config);
     }
 };
 
-TEST_F(PluginConfigCommandTest, set)
-{
-    try {
-        m_irccd.plugins().add(std::make_unique<CustomPlugin>("test"));
-        m_irccdctl.client().request({
-            { "command", "plugin-config" },
-            { "plugin", "test" },
-            { "variable", "verbosy" },
-            { "value", "falsy" }
-        });
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(plugin_config_test_suite, command_test<plugin_config_command>)
 
-        poll([&] {
-            return !m_irccd.plugins().require("test")->config().empty();
-        });
-
-        auto config = m_irccd.plugins().require("test")->config();
+BOOST_AUTO_TEST_CASE(set)
+{
+    daemon_->plugins().add(std::make_unique<custom_plugin>("test"));
+    ctl_->send({
+        { "command",    "plugin-config" },
+        { "plugin",     "test"          },
+        { "variable",   "verbosy"       },
+        { "value",      "falsy"         }
+    });
 
-        ASSERT_FALSE(config.empty());
-        ASSERT_EQ("falsy", config["verbosy"]);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] {
+        return !daemon_->plugins().require("test")->config().empty();
+    });
+
+    auto config = daemon_->plugins().require("test")->config();
+
+    BOOST_TEST(!config.empty());
+    BOOST_TEST(config["verbosy"] == "falsy");
 }
 
-TEST_F(PluginConfigCommandTest, get)
+BOOST_AUTO_TEST_CASE(get)
 {
-    try {
-        auto plugin = std::make_unique<CustomPlugin>("test");
-        auto json = nlohmann::json();
+    auto plugin = std::make_unique<custom_plugin>("test");
+    auto json = nlohmann::json();
 
-        plugin->set_config({
-            { "x1", "10" },
-            { "x2", "20" }
-        });
+    plugin->set_config({
+        { "x1", "10" },
+        { "x2", "20" }
+    });
 
-        m_irccd.plugins().add(std::move(plugin));
-        m_irccdctl.client().request({
-            { "command", "plugin-config" },
-            { "plugin", "test" },
-            { "variable", "x1" }
-        });
-        m_irccdctl.client().onMessage.connect([&] (auto message) {
-            json = std::move(message);
-        });
+    daemon_->plugins().add(std::move(plugin));
+    ctl_->send({
+        { "command", "plugin-config" },
+        { "plugin", "test" },
+        { "variable", "x1" }
+    });
+    ctl_->recv([&] (auto, auto message) {
+        json = std::move(message);
+    });
 
-        poll([&] {
-            return json.is_object();
-        });
+    wait_for([&] {
+        return json.is_object();
+    });
 
-        ASSERT_TRUE(json.is_object());
-        ASSERT_EQ("10", json["variables"]["x1"]);
-        ASSERT_TRUE(json["variables"]["x2"].is_null());
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json["variables"]["x1"].get<std::string>() == "10");
+    BOOST_TEST(json["variables"]["x2"].is_null());
 }
 
-TEST_F(PluginConfigCommandTest, getAll)
+BOOST_AUTO_TEST_CASE(getall)
 {
-    try {
-        auto plugin = std::make_unique<CustomPlugin>("test");
-        auto json = nlohmann::json();
+    auto plugin = std::make_unique<custom_plugin>("test");
+    auto json = nlohmann::json();
 
-        plugin->set_config({
-            { "x1", "10" },
-            { "x2", "20" }
-        });
+    plugin->set_config({
+        { "x1", "10" },
+        { "x2", "20" }
+    });
 
-        m_irccd.plugins().add(std::move(plugin));
-        m_irccdctl.client().request({
-            { "command", "plugin-config" },
-            { "plugin", "test" }
-        });
-        m_irccdctl.client().onMessage.connect([&] (auto message) {
-            json = std::move(message);
-        });
+    daemon_->plugins().add(std::move(plugin));
+    ctl_->send({
+        { "command", "plugin-config" },
+        { "plugin", "test" }
+    });
+    ctl_->recv([&] (auto, auto message) {
+        json = std::move(message);
+    });
 
-        poll([&] {
-            return json.is_object();
-        });
+    wait_for([&] {
+        return json.is_object();
+    });
 
-        ASSERT_TRUE(json.is_object());
-        ASSERT_EQ("10", json["variables"]["x1"]);
-        ASSERT_EQ("20", json["variables"]["x2"]);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json["variables"]["x1"].get<std::string>() == "10");
+    BOOST_TEST(json["variables"]["x2"].get<std::string>() == "20");
 }
 
-} // !namespace
+BOOST_AUTO_TEST_SUITE_END()
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
-
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-plugin-info/main.cpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/tests/cmd-plugin-info/main.cpp	Fri Nov 24 21:04:32 2017 +0100
@@ -16,88 +16,70 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
-#include <plugin.hpp>
+#define BOOST_TEST_MODULE "plugin-info"
+#include <boost/test/unit_test.hpp>
 
-using namespace irccd;
+#include <irccd/command.hpp>
+#include <irccd/plugin_service.hpp>
 
-namespace {
+#include <command_test.hpp>
 
-class PluginInfoCommandTest : public CommandTester {
-public:
-    PluginInfoCommandTest()
-        : CommandTester(std::make_unique<plugin_info_command>())
-    {
-    }
-};
+namespace irccd {
+
+BOOST_FIXTURE_TEST_SUITE(plugin_info_test_suite, command_test<plugin_info_command>)
 
-TEST_F(PluginInfoCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        auto plg = std::make_unique<plugin>("test", "");
-        auto response = nlohmann::json();
+    auto plg = std::make_unique<plugin>("test", "");
+    auto response = nlohmann::json();
 
-        plg->set_author("Francis Beaugrand");
-        plg->set_license("GPL");
-        plg->set_summary("Completely useless plugin");
-        plg->set_version("0.0.0.0.0.0.0.0.1-beta5");
+    plg->set_author("Francis Beaugrand");
+    plg->set_license("GPL");
+    plg->set_summary("Completely useless plugin");
+    plg->set_version("0.0.0.0.0.0.0.0.1-beta5");
 
-        m_irccd.plugins().add(std::move(plg));
-        m_irccdctl.client().onMessage.connect([&] (auto msg) {
-            response = std::move(msg);
-        });
-        m_irccdctl.client().request({
-            { "command",    "plugin-info"       },
-            { "plugin",     "test"              },
-        });
+    daemon_->plugins().add(std::move(plg));
+    ctl_->recv([&] (auto, auto msg) {
+        response = std::move(msg);
+    });
+    ctl_->send({
+        { "command",    "plugin-info"       },
+        { "plugin",     "test"              },
+    });
 
-        poll([&] () {
-            return response.is_object();
-        });
+    wait_for([&] () {
+        return response.is_object();
+    });
 
-        ASSERT_TRUE(response.is_object());
-        ASSERT_EQ("Francis Beaugrand", response["author"]);
-        ASSERT_EQ("GPL", response["license"]);
-        ASSERT_EQ("Completely useless plugin", response["summary"]);
-        ASSERT_EQ("0.0.0.0.0.0.0.0.1-beta5", response["version"]);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    BOOST_TEST(response.is_object());
+    BOOST_TEST(response["author"].get<std::string>() == "Francis Beaugrand");
+    BOOST_TEST(response["license"].get<std::string>() == "GPL");
+    BOOST_TEST(response["summary"].get<std::string>() == "Completely useless plugin");
+    BOOST_TEST(response["version"].get<std::string>() == "0.0.0.0.0.0.0.0.1-beta5");
 }
 
-TEST_F(PluginInfoCommandTest, notfound)
+BOOST_AUTO_TEST_CASE(notfound)
 {
-    try {
-        auto response = nlohmann::json();
+    auto response = nlohmann::json();
 
-        m_irccdctl.client().onMessage.connect([&] (auto msg) {
-            response = std::move(msg);
-        });
-        m_irccdctl.client().request({
-            { "command",    "plugin-info"       },
-            { "plugin",     "test"              },
-        });
+    ctl_->recv([&] (auto, auto msg) {
+        response = std::move(msg);
+    });
+    ctl_->send({
+        { "command",    "plugin-info"       },
+        { "plugin",     "test"              },
+    });
 
-        poll([&] () {
-            return response.is_object();
-        });
+    wait_for([&] () {
+        return response.is_object();
+    });
 
-        ASSERT_TRUE(response.is_object());
-        ASSERT_FALSE(response["status"]);
-        ASSERT_EQ("plugin test not found", response["error"]);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    BOOST_TEST(response.is_object());
+
+    // TODO: error code here.
+    BOOST_TEST(response["error"].get<std::string>() == "plugin test not found");
 }
 
-} // !namespace
+BOOST_AUTO_TEST_SUITE_END()
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
-
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-plugin-list/main.cpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/tests/cmd-plugin-list/main.cpp	Fri Nov 24 21:04:32 2017 +0100
@@ -16,53 +16,45 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
-#include <plugin.hpp>
+#define BOOST_TEST_MODULE "plugin-list"
+#include <boost/test/unit_test.hpp>
 
-using namespace irccd;
+#include <irccd/command.hpp>
+#include <irccd/plugin_service.hpp>
+
+#include <command_test.hpp>
 
-namespace {
+namespace irccd {
 
-class PluginListCommandTest : public CommandTester {
+class plugin_list_test : public command_test<plugin_list_command> {
 public:
-    PluginListCommandTest()
-        : CommandTester(std::make_unique<plugin_list_command>())
+    plugin_list_test()
     {
-        m_irccd.plugins().add(std::make_unique<plugin>("t1", ""));
-        m_irccd.plugins().add(std::make_unique<plugin>("t2", ""));
+        daemon_->plugins().add(std::make_unique<plugin>("t1", ""));
+        daemon_->plugins().add(std::make_unique<plugin>("t2", ""));
     }
 };
 
-TEST_F(PluginListCommandTest, basic)
-{
-    try {
-        auto response = nlohmann::json();
+BOOST_FIXTURE_TEST_SUITE(plugin_list_test_suite, plugin_list_test)
 
-        m_irccdctl.client().onMessage.connect([&] (auto message) {
-            response = message;
-        });
-        m_irccdctl.client().request({{ "command", "plugin-list" }});
+BOOST_AUTO_TEST_CASE(basic)
+{
+    auto response = nlohmann::json();
 
-        poll([&] () {
-            return response.is_object();
-        });
+    ctl_->send({{"command", "plugin-list"}});
+    ctl_->recv([&] (auto code, auto message) {
+        response = message;
+    });
 
-        ASSERT_TRUE(response.is_object());
-        ASSERT_EQ("t1", response["list"][0]);
-        ASSERT_EQ("t2", response["list"][1]);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return response.is_object();
+    });
+
+    BOOST_TEST(response.is_object());
+    BOOST_TEST("t1", response["list"][0]);
+    BOOST_TEST("t2", response["list"][1]);
 }
 
-} // !namespace
+BOOST_AUTO_TEST_SUITE_END()
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
-
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-plugin-load/main.cpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/tests/cmd-plugin-load/main.cpp	Fri Nov 24 21:04:32 2017 +0100
@@ -16,63 +16,59 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
-#include <plugin.hpp>
+#define BOOST_TEST_MODULE "plugin-load"
+#include <boost/test/unit_test.hpp>
 
-using namespace irccd;
+#include <irccd/command.hpp>
+#include <irccd/plugin_service.hpp>
+
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-class CustomLoader : public plugin_loader {
+class custom_loader : public plugin_loader {
 public:
-    std::shared_ptr<plugin> open(const std::string &,
-                                 const std::string &) noexcept override
+    std::shared_ptr<plugin> open(const std::string&,
+                                 const std::string&) noexcept override
     {
         return nullptr;
     }
 
-    std::shared_ptr<plugin> find(const std::string &id) noexcept override
+    std::shared_ptr<plugin> find(const std::string& id) noexcept override
     {
         return std::make_unique<plugin>(id, "");
     }
 };
 
-class PluginLoadCommandTest : public CommandTester {
+class plugin_load_test : public command_test<plugin_load_command> {
 public:
-    PluginLoadCommandTest()
-        : CommandTester(std::make_unique<plugin_load_command>())
+    plugin_load_test()
     {
-        m_irccd.plugins().add_loader(std::make_unique<CustomLoader>());
+        daemon_->plugins().add_loader(std::make_unique<custom_loader>());
     }
 };
 
-TEST_F(PluginLoadCommandTest, basic)
-{
-    try {
-        m_irccdctl.client().request({
-            { "command", "plugin-load" },
-            { "plugin", "foo" }
-        });
+} // !irccd
+
+BOOST_FIXTURE_TEST_SUITE(plugin_load_test_suite, plugin_load_test)
 
-        poll([&] () {
-            return m_irccd.plugins().has("foo");
-        });
+BOOST_AUTO_TEST_CASE(basic)
+{
+    ctl_->send({
+        { "command", "plugin-load" },
+        { "plugin", "foo" }
+    });
 
-        ASSERT_FALSE(m_irccd.plugins().list().empty());
-        ASSERT_TRUE(m_irccd.plugins().has("foo"));
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return daemon_->plugins().has("foo");
+    });
+
+    BOOST_TEST(!daemon_->plugins().list().empty());
+    BOOST_TEST(daemon_->plugins().has("foo"));
 }
 
-} // !namespace
+BOOST_AUTO_TEST_SUITE_END()
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
-
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-plugin-reload/main.cpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/tests/cmd-plugin-reload/main.cpp	Fri Nov 24 21:04:32 2017 +0100
@@ -16,89 +16,83 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
-#include <plugin.hpp>
+#define BOOST_TEST_MODULE "plugin-reload"
+#include <boost/test/unit_test.hpp>
 
-using namespace irccd;
+#include <irccd/command.hpp>
+#include <irccd/plugin_service.hpp>
+
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-bool called = false;
+class custom_plugin : public plugin {
+public:
+    bool reloaded{false};
 
-class CustomPlugin : public plugin {
-public:
-    CustomPlugin()
+    custom_plugin()
         : plugin("test", "")
     {
     }
 
-    void on_reload(irccd::irccd &) override
+    void on_reload(irccd&) override
     {
-        called = true;
+        reloaded = true;
     }
 };
 
-class PluginReloadCommandTest : public CommandTester {
-public:
-    PluginReloadCommandTest()
-        : CommandTester(std::make_unique<plugin_reload_command>())
+class plugin_reload_test : public command_test<plugin_reload_command> {
+protected:
+    std::shared_ptr<custom_plugin> plugin_;
+
+    plugin_reload_test()
+        : plugin_(std::make_shared<custom_plugin>())
     {
-        called = false;
+        daemon_->plugins().add(plugin_);
     }
 };
 
-TEST_F(PluginReloadCommandTest, basic)
-{
-    try {
-        m_irccd.plugins().add(std::make_unique<CustomPlugin>());
-        m_irccdctl.client().request({
-            { "command", "plugin-reload" },
-            { "plugin", "test" }
-        });
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(plugin_reload_test_suite, plugin_reload_test)
 
-        poll([&] () {
-            return called;
-        });
+BOOST_AUTO_TEST_CASE(basic)
+{
+    ctl_->send({
+        { "command",    "plugin-reload" },
+        { "plugin",     "test"          }
+    });
 
-        ASSERT_TRUE(called);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return plugin_->reloaded;
+    });
+
+    BOOST_TEST(plugin_->reloaded);
 }
 
-TEST_F(PluginReloadCommandTest, notfound)
+BOOST_AUTO_TEST_CASE(notfound)
 {
-    try {
-        auto response = nlohmann::json();
+    auto response = nlohmann::json();
 
-        m_irccdctl.client().onMessage.connect([&] (auto msg) {
-            response = msg;
-        });
-        m_irccdctl.client().request({
-            { "command", "plugin-reload" },
-            { "plugin", "no" }
-        });
+    ctl_->recv([&] (auto, auto msg) {
+        response = msg;
+    });
+    ctl_->send({
+        { "command",    "plugin-reload" },
+        { "plugin",     "no"            }
+    });
 
-        poll([&] () {
-            return response.is_object();
-        });
+    wait_for([&] () {
+        return response.is_object();
+    });
 
-        ASSERT_TRUE(response.is_object());
-        ASSERT_FALSE(response["status"]);
-        ASSERT_EQ("plugin no not found", response["error"]);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    // TODO: error code
+    BOOST_TEST(response.is_object());
+    BOOST_TEST(response["error"].get<std::string>() == "plugin no not found");
 }
 
-} // !namespace
+BOOST_AUTO_TEST_SUITE_END()
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
-
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-plugin-unload/main.cpp	Fri Nov 24 20:30:42 2017 +0100
+++ b/tests/cmd-plugin-unload/main.cpp	Fri Nov 24 21:04:32 2017 +0100
@@ -16,65 +16,62 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
-#include <plugin.hpp>
+#define BOOST_TEST_MODULE "plugin-unload"
+#include <boost/test/unit_test.hpp>
 
-using namespace irccd;
+#include <irccd/command.hpp>
+#include <irccd/plugin_service.hpp>
+
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-bool called = false;
+class custom_plugin : public plugin {
+public:
+    bool unloaded{false};
 
-class CustomPlugin : public plugin {
-public:
-    CustomPlugin()
+    custom_plugin()
         : plugin("test", "")
     {
     }
 
-    void on_unload(irccd::irccd &) override
+    void on_unload(irccd &) override
     {
-        called = true;
+        unloaded = true;
     }
 };
 
-class PluginUnloadCommandTest : public CommandTester {
-public:
-    PluginUnloadCommandTest()
-        : CommandTester(std::make_unique<plugin_unload_command>())
+class plugin_unload_test : public command_test<plugin_unload_command> {
+protected:
+    std::shared_ptr<custom_plugin> plugin_;
+
+    plugin_unload_test()
+        : plugin_(std::make_shared<custom_plugin>())
     {
-        called = false;
+        daemon_->plugins().add(plugin_);
     }
 };
 
-TEST_F(PluginUnloadCommandTest, basic)
-{
-    try {
-        m_irccd.plugins().add(std::make_unique<CustomPlugin>());
-        m_irccdctl.client().request({
-            { "command", "plugin-unload" },
-            { "plugin", "test" }
-        });
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(plugin_unload_test_suite, plugin_unload_test)
 
-        poll([&] () {
-            return called;
-        });
+BOOST_AUTO_TEST_CASE(basic)
+{
+    ctl_->send({
+        { "command",    "plugin-unload" },
+        { "plugin",     "test"          }
+    });
 
-        ASSERT_TRUE(called);
-        ASSERT_TRUE(m_irccd.plugins().list().empty());
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return plugin_->unloaded;
+    });
+
+    BOOST_TEST(plugin_->unloaded);
 }
 
-} // !namespace
+BOOST_AUTO_TEST_SUITE_END()
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
-
-    return RUN_ALL_TESTS();
-}
+} // !irccd