summaryrefslogtreecommitdiff
path: root/test/test_daemon.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2021-11-26 08:19:58 +0100
committerJoel Klinghed <the_jk@spawned.biz>2021-11-26 08:19:58 +0100
commitf70495a48646e54272783b4b709aca0396cb85f8 (patch)
tree5e63b1f57582b3f025f1008034e4b066db0c32a6 /test/test_daemon.cc
parent9c26f52e0942e3ddc8fe90fad5da871324c66f08 (diff)
Create daemon module and use it from server
Need to run setup() after forking, otherwise each TaskRunner created in setup() will dead-lock at exit as there are now two copies of each of them but not of the threads causing the destructors to lock. This made setup a little bit more complicated as it has to forward the log and status to parent process but I turned out quite nice.
Diffstat (limited to 'test/test_daemon.cc')
-rw-r--r--test/test_daemon.cc146
1 files changed, 146 insertions, 0 deletions
diff --git a/test/test_daemon.cc b/test/test_daemon.cc
new file mode 100644
index 0000000..04ba105
--- /dev/null
+++ b/test/test_daemon.cc
@@ -0,0 +1,146 @@
+#include "common.hh"
+
+#include "daemon.hh"
+#include "logger.hh"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <memory>
+
+namespace {
+
+class MockDaemon : public Daemon {
+public:
+ MOCK_METHOD(bool, setup, (Logger*), (override));
+ MOCK_METHOD(bool, run, (), (override));
+};
+
+class MockLogger : public Logger {
+public:
+ void err(char const* format, ...) override {
+ va_list args;
+ va_start(args, format);
+ vlog("err", format, args);
+ va_end(args);
+ }
+
+ void warn(char const* format, ...) override {
+ va_list args;
+ va_start(args, format);
+ vlog("warn", format, args);
+ va_end(args);
+ }
+
+ void info(char const* format, ...) override {
+ va_list args;
+ va_start(args, format);
+ vlog("info", format, args);
+ va_end(args);
+ }
+
+ void dbg(char const* format, ...) override {
+ va_list args;
+ va_start(args, format);
+ vlog("dbg", format, args);
+ va_end(args);
+ }
+
+ void vlog(std::string level, char const* format, va_list args) {
+ if (strcmp(format, "%s") == 0) {
+ log(level, va_arg(args, char const*));
+ } else if (strcmp(format, "%.*s") == 0) {
+ auto len = va_arg(args, int);
+ auto ptr = va_arg(args, char const*);
+ log(level, std::string(ptr, len));
+ } else {
+ log(level, format);
+ }
+ }
+
+ MOCK_METHOD(void, log, (std::string, std::string));
+};
+
+class TestDaemon : public Daemon {
+public:
+ bool setup(Logger*) override {
+ return false;
+ }
+
+ bool run() override {
+ return false;
+ }
+};
+
+class FailSetupDaemon : public TestDaemon {
+public:
+ explicit FailSetupDaemon(std::string error)
+ : error_(std::move(error)) {}
+
+ bool setup(Logger* logger) override {
+ logger->err("%s", error_.c_str());
+ return false;
+ }
+
+private:
+ std::string error_;
+};
+
+class SuccessSetupDaemon : public TestDaemon {
+public:
+ explicit SuccessSetupDaemon(std::string success)
+ : success_(std::move(success)) {}
+
+ bool setup(Logger* logger) override {
+ logger->info("%s", success_.c_str());
+ return true;
+ }
+
+private:
+ std::string success_;
+};
+
+} // namespace
+
+TEST(daemon, run_in_foreground_setup_fail) {
+ auto daemon = std::make_unique<MockDaemon>();
+ auto logger = Logger::create_null();
+ EXPECT_CALL(*daemon, setup(logger.get()))
+ .WillOnce(testing::Return(false));
+ EXPECT_FALSE(Daemon::run_in_foreground(logger.get(), std::move(daemon)));
+}
+
+TEST(daemon, run_in_foreground_run_fail) {
+ auto daemon = std::make_unique<MockDaemon>();
+ auto logger = Logger::create_null();
+ EXPECT_CALL(*daemon, setup(logger.get()))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(*daemon, run())
+ .WillOnce(testing::Return(false));
+ EXPECT_FALSE(Daemon::run_in_foreground(logger.get(), std::move(daemon)));
+}
+
+TEST(daemon, run_in_foreground_run_success) {
+ auto daemon = std::make_unique<MockDaemon>();
+ auto logger = Logger::create_null();
+ EXPECT_CALL(*daemon, setup(logger.get()))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(*daemon, run())
+ .WillOnce(testing::Return(true));
+ EXPECT_TRUE(Daemon::run_in_foreground(logger.get(), std::move(daemon)));
+}
+
+TEST(daemon, fork_in_background_setup_fail) {
+ auto daemon = std::make_unique<FailSetupDaemon>("Something failed");
+ MockLogger logger;
+ testing::Mock::AllowLeak(&logger); // Forking copies the mock.
+ EXPECT_CALL(logger, log("err", "Something failed"));
+ EXPECT_FALSE(Daemon::fork_in_background(&logger, std::move(daemon)));
+}
+
+TEST(daemon, fork_in_background_run_fail) {
+ auto daemon = std::make_unique<SuccessSetupDaemon>("All good");
+ MockLogger logger;
+ testing::Mock::AllowLeak(&logger); // Forking copies the mock.
+ EXPECT_CALL(logger, log("info", "All good"));
+ EXPECT_TRUE(Daemon::fork_in_background(&logger, std::move(daemon)));
+}