#include "common.hh" #include "daemon.hh" #include "logger.hh" #include #include #include #include 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); } #ifndef NDEBUG void dbg(char const* format, ...) override { va_list args; va_start(args, format); vlog("dbg", format, args); va_end(args); } #endif 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(); 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(); 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(); 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("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("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))); }