1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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)));
}
|