diff tests/src/plugins/tictactoe/main.cpp @ 632:e5d0f4289e04

Plugin tictactoe: brand new plugin, closes #393 @6h
author David Demelier <markand@malikania.fr>
date Tue, 13 Mar 2018 13:51:17 +0100
parents
children c07819d1d306
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/src/plugins/tictactoe/main.cpp	Tue Mar 13 13:51:17 2018 +0100
@@ -0,0 +1,257 @@
+/*
+ * main.cpp -- test plugin 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.
+ */
+
+#define BOOST_TEST_MODULE "Plugin tictactoe"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/string_util.hpp>
+
+#include <irccd/daemon/irccd.hpp>
+#include <irccd/daemon/server.hpp>
+#include <irccd/daemon/service/plugin_service.hpp>
+
+#include <irccd/test/plugin_test.hpp>
+
+namespace irccd {
+
+class test_fixture : public plugin_test {
+public:
+    test_fixture()
+        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+    {
+        plugin_->set_formats({
+            { "draw",       "draw=#{channel}:#{command}:#{nickname}:#{plugin}:#{server}"                },
+            { "invalid",    "invalid=#{channel}:#{command}:#{nickname}:#{origin}:#{plugin}:#{server}"   },
+            { "running",    "running=#{channel}:#{command}:#{nickname}:#{origin}:#{plugin}:#{server}"   },
+            { "turn",       "turn=#{channel}:#{command}:#{nickname}:#{plugin}:#{server}"                },
+            { "used",       "used=#{channel}:#{command}:#{nickname}:#{origin}:#{plugin}:#{server}"      },
+            { "win",        "win=#{channel}:#{command}:#{nickname}:#{plugin}:#{server}"                 }
+        });
+    }
+
+    auto next_players() const
+    {
+        if (server_->cqueue().size() == 0)
+            throw std::runtime_error("no message");
+
+        const auto cmd = server_->cqueue().back();
+        const auto list = string_util::split(cmd["message"].get<std::string>(), ":");
+
+        BOOST_TEST(list.size() == 5U);
+        BOOST_TEST(list[0] == "turn=#tictactoe");
+        BOOST_TEST(list[1] == "!tictactoe");
+        BOOST_TEST(list[3] == "tictactoe");
+        BOOST_TEST(list[4] == "test");
+
+        return list[2] == "a" ? std::make_pair("a", "b") : std::make_pair("b", "a");
+    }
+
+    auto start()
+    {
+        plugin_->on_command(irccd_, {server_, "a!a@localhost", "#tictactoe", "b"});
+        plugin_->on_names(irccd_, {server_, "#tictactoe", {"a", "b"}});
+
+        return next_players();
+    }
+
+    /**
+     * Helper to place several tokens on the board and automatically toggling
+     * players.
+     *
+     * This will start the game from "a" with target opponent "b".
+     *
+     */
+    void run(const std::initializer_list<std::string>& points)
+    {
+        auto players = start();
+
+        for (const auto& p : points) {
+            server_->cqueue().clear();
+            plugin_->on_message(irccd_, {server_, players.first, "#tictactoe", p});
+            players = next_players();
+        }
+    }
+};
+
+BOOST_FIXTURE_TEST_SUITE(test_fixture_suite, test_fixture)
+
+BOOST_AUTO_TEST_CASE(win)
+{
+    run({"a 1", "b 1", "a 2", "b 2"});
+
+    const auto players = next_players();
+
+    plugin_->on_message(irccd_, {server_, players.first, "#tictactoe", "a 3"});
+
+    const auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd.is_object());
+    BOOST_TEST(cmd["command"].get<std::string>() == "message");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+
+    const auto parts = string_util::split(cmd["message"].get<std::string>(), ":");
+
+    BOOST_TEST(parts.size() == 5U);
+    BOOST_TEST(parts[0] == "win=#tictactoe");
+    BOOST_TEST(parts[1] == "!tictactoe");
+    BOOST_TEST(parts[2] == players.first);
+    BOOST_TEST(parts[3] == "tictactoe");
+    BOOST_TEST(parts[4] == "test");
+}
+
+BOOST_AUTO_TEST_CASE(draw)
+{
+    /*
+     *   a b c
+     * 1 o x o
+     * 2 o x x
+     * 3 x o x
+     */
+    run({ "b 2", "c 1", "c 3", "b 3", "c 2", "a 2", "a 3", "a 1" });
+
+    const auto players = next_players();
+
+    plugin_->on_message(irccd_, {server_, players.first, "#tictactoe", "b 1"});
+
+    const auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd.is_object());
+    BOOST_TEST(cmd["command"].get<std::string>() == "message");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+
+    const auto parts = string_util::split(cmd["message"].get<std::string>(), ":");
+
+    BOOST_TEST(parts.size() == 5U);
+    BOOST_TEST(parts[0] == "draw=#tictactoe");
+    BOOST_TEST(parts[1] == "!tictactoe");
+    BOOST_TEST(parts[2] == players.first);
+    BOOST_TEST(parts[3] == "tictactoe");
+    BOOST_TEST(parts[4] == "test");
+}
+
+BOOST_AUTO_TEST_CASE(used)
+{
+    auto players = start();
+
+    plugin_->on_message(irccd_, {server_, players.first, "#tictactoe", "a 1"});
+    plugin_->on_message(irccd_, {server_, players.second, "#tictactoe", "a 1"});
+
+    const auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd.is_object());
+    BOOST_TEST(cmd["command"].get<std::string>() == "message");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+
+    const auto parts = string_util::split(cmd["message"].get<std::string>(), ":");
+
+    BOOST_TEST(parts[0] == "used=#tictactoe");
+    BOOST_TEST(parts[1] == "!tictactoe");
+    BOOST_TEST(parts[2] == players.second);
+    BOOST_TEST(parts[3] == players.second);
+    BOOST_TEST(parts[4] == "tictactoe");
+    BOOST_TEST(parts[5] == "test");
+}
+
+BOOST_AUTO_TEST_CASE(invalid)
+{
+    nlohmann::json cmd;
+
+    // empty name (no names)
+    plugin_->on_command(irccd_, {server_, "jean", "#tictactoe", ""});
+    cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "message");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+    BOOST_TEST(cmd["message"].get<std::string>() == "invalid=#tictactoe:!tictactoe:jean:jean:tictactoe:test");
+
+    // bot name (no names)
+    plugin_->on_command(irccd_, {server_, "jean", "#tictactoe", "irccd"});
+    cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "message");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+    BOOST_TEST(cmd["message"].get<std::string>() == "invalid=#tictactoe:!tictactoe:jean:jean:tictactoe:test");
+
+    // target is origin (no names)
+    plugin_->on_command(irccd_, {server_, server_->nickname(), "#tictactoe", server_->nickname()});
+    cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "message");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+    BOOST_TEST(cmd["message"].get<std::string>() == "invalid=#tictactoe:!tictactoe:irccd:irccd:tictactoe:test");
+
+    // not existing (names)
+    plugin_->on_command(irccd_, {server_, server_->nickname(), "#tictactoe", server_->nickname()});
+    plugin_->on_names(irccd_, {server_, "#tictactoe", {"a", "b", "c"}});
+    cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "message");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+    BOOST_TEST(cmd["message"].get<std::string>() == "invalid=#tictactoe:!tictactoe:irccd:irccd:tictactoe:test");
+}
+
+BOOST_AUTO_TEST_CASE(random)
+{
+    /*
+     * Ensure that the first player is not always the originator, start the game
+     * for at most 1'000'000 times to avoid forever loop.
+     */
+    unsigned count = 0;
+    bool a = false;
+    bool b = false;
+
+    // Last player turn is the winner.
+    while (!a && !b && count++ < 1000000U) {
+        run({"a 1", "b 1", "a 2", "b 2"});
+
+        const auto players = next_players();
+
+        if (players.first == std::string("a"))
+            a = true;
+        else
+            b = true;
+
+        plugin_->on_message(irccd_, {server_, players.first, "#tictactoe", "a 3"});
+    }
+}
+
+BOOST_AUTO_TEST_CASE(kick)
+{
+    auto players = start();
+
+    server_->cqueue().clear();
+    plugin_->on_kick(irccd_, {server_, "kefka", "#tictactoe", players.first, ""});
+    plugin_->on_message(irccd_, {server_, players.first, "#tictactoe", "a 1"});
+
+    BOOST_TEST(server_->cqueue().empty());
+}
+
+BOOST_AUTO_TEST_CASE(part)
+{
+    auto players = start();
+
+    server_->cqueue().clear();
+    plugin_->on_part(irccd_, {server_, players.first, "#tictactoe", ""});
+    plugin_->on_message(irccd_, {server_, players.first, "#tictactoe", "a 1"});
+
+    BOOST_TEST(server_->cqueue().empty());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // !irccd