Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
test_process_metrics_collector.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
5#include <gtest/gtest.h>
7
8#include <fstream>
9
10namespace kcenon {
11namespace monitoring {
12namespace {
13
14class ProcessMetricsCollectorTest : public ::testing::Test {
15 protected:
16 void SetUp() override {
17 collector_ = std::make_unique<process_metrics_collector>();
18 std::unordered_map<std::string, std::string> config;
19 collector_->initialize(config);
20 }
21
22 std::unique_ptr<process_metrics_collector> collector_;
23};
24
25TEST_F(ProcessMetricsCollectorTest, InitializesSuccessfully) {
26 EXPECT_NE(collector_, nullptr);
27 EXPECT_EQ(collector_->name(), "process_metrics_collector");
28}
29
30TEST_F(ProcessMetricsCollectorTest, ReturnsCorrectMetricTypes) {
31 auto types = collector_->get_metric_types();
32 EXPECT_FALSE(types.empty());
33
34 std::vector<std::string> expected = {
35 "process.fd.open_count",
36 "process.fd.usage_percent",
37 "process.fs.inodes_total",
38 "process.fs.inodes_used",
39 "process.context_switches.total",
40 "process.context_switches.voluntary"
41 };
42
43 for (const auto& expected_type : expected) {
44 EXPECT_NE(std::find(types.begin(), types.end(), expected_type), types.end())
45 << "Missing metric type: " << expected_type;
46 }
47}
48
49TEST_F(ProcessMetricsCollectorTest, ConfigurationOptions) {
50 auto collector = std::make_unique<process_metrics_collector>();
51 std::unordered_map<std::string, std::string> config;
52 config["fd_warning_threshold"] = "70.0";
53 config["fd_critical_threshold"] = "90.0";
54 config["collect_inodes"] = "false";
55 EXPECT_TRUE(collector->initialize(config));
56
57 auto stats = collector->get_statistics();
58 EXPECT_DOUBLE_EQ(stats["fd_warning_threshold"], 70.0);
59 EXPECT_DOUBLE_EQ(stats["fd_critical_threshold"], 90.0);
60 EXPECT_DOUBLE_EQ(stats["collect_inodes"], 0.0);
61}
62
63TEST_F(ProcessMetricsCollectorTest, CanBeDisabled) {
64 auto collector = std::make_unique<process_metrics_collector>();
65 std::unordered_map<std::string, std::string> config;
66 config["enabled"] = "false";
67 collector->initialize(config);
68
69 auto metrics = collector->collect();
70 EXPECT_TRUE(metrics.empty());
71
72 auto stats = collector->get_statistics();
73 EXPECT_DOUBLE_EQ(stats["enabled"], 0.0);
74}
75
76TEST_F(ProcessMetricsCollectorTest, TracksStatistics) {
77 collector_->collect();
78 collector_->collect();
79
80 auto stats = collector_->get_statistics();
81 EXPECT_GE(stats["collection_count"], 2.0);
82 EXPECT_GE(stats["collection_errors"], 0.0);
83}
84
85TEST_F(ProcessMetricsCollectorTest, CollectReturnsMetrics) {
86 EXPECT_NO_THROW(collector_->collect());
87}
88
89TEST_F(ProcessMetricsCollectorTest, GetLastMetrics) {
90 collector_->collect();
91 auto last = collector_->get_last_metrics();
92
93 auto now = std::chrono::system_clock::now();
94 auto diff = std::chrono::duration_cast<std::chrono::seconds>(now - last.timestamp);
95 EXPECT_LT(diff.count(), 10);
96}
97
98TEST_F(ProcessMetricsCollectorTest, MonitoringAvailabilityChecks) {
99 EXPECT_NO_THROW(collector_->is_fd_monitoring_available());
100 EXPECT_NO_THROW(collector_->is_inode_monitoring_available());
101 EXPECT_NO_THROW(collector_->is_context_switch_monitoring_available());
102}
103
104TEST_F(ProcessMetricsCollectorTest, SelectiveCollectionFdOnly) {
105 auto collector = std::make_unique<process_metrics_collector>();
106 std::unordered_map<std::string, std::string> config;
107 config["collect_fd"] = "true";
108 config["collect_inodes"] = "false";
109 config["collect_context_switches"] = "false";
110 collector->initialize(config);
111
112 auto types = collector->get_metric_types();
113 bool has_fd_metric = false;
114 bool has_inode_metric = false;
115 bool has_cs_metric = false;
116
117 for (const auto& t : types) {
118 if (t.find("process.fd.") != std::string::npos) has_fd_metric = true;
119 if (t.find("process.fs.") != std::string::npos) has_inode_metric = true;
120 if (t.find("process.context_switches.") != std::string::npos) has_cs_metric = true;
121 }
122
123 EXPECT_TRUE(has_fd_metric);
124 EXPECT_FALSE(has_inode_metric);
125 EXPECT_FALSE(has_cs_metric);
126}
127
128TEST_F(ProcessMetricsCollectorTest, SelectiveCollectionInodesOnly) {
129 auto collector = std::make_unique<process_metrics_collector>();
130 std::unordered_map<std::string, std::string> config;
131 config["collect_fd"] = "false";
132 config["collect_inodes"] = "true";
133 config["collect_context_switches"] = "false";
134 collector->initialize(config);
135
136 auto types = collector->get_metric_types();
137 bool has_fd_metric = false;
138 bool has_inode_metric = false;
139 bool has_cs_metric = false;
140
141 for (const auto& t : types) {
142 if (t.find("process.fd.") != std::string::npos) has_fd_metric = true;
143 if (t.find("process.fs.") != std::string::npos) has_inode_metric = true;
144 if (t.find("process.context_switches.") != std::string::npos) has_cs_metric = true;
145 }
146
147 EXPECT_FALSE(has_fd_metric);
148 EXPECT_TRUE(has_inode_metric);
149 EXPECT_FALSE(has_cs_metric);
150}
151
152TEST_F(ProcessMetricsCollectorTest, MultipleCollectionsAreStable) {
153 for (int i = 0; i < 10; ++i) {
154 auto metrics = collector_->collect();
155 EXPECT_NO_THROW(collector_->get_statistics());
156 }
157
158 auto stats = collector_->get_statistics();
159 EXPECT_GE(stats["collection_count"], 10.0);
160}
161
162TEST_F(ProcessMetricsCollectorTest, MetricsHaveCorrectTags) {
163 auto metrics = collector_->collect();
164
165 for (const auto& m : metrics) {
166 auto it = m.tags.find("collector");
167 if (it != m.tags.end()) {
168 EXPECT_EQ(it->second, "process_metrics_collector");
169 }
170 }
171}
172
173TEST_F(ProcessMetricsCollectorTest, IsHealthyReflectsState) {
174 EXPECT_NO_THROW(collector_->is_healthy());
175
176 auto disabled_collector = std::make_unique<process_metrics_collector>();
177 std::unordered_map<std::string, std::string> config;
178 config["enabled"] = "false";
179 disabled_collector->initialize(config);
180 EXPECT_TRUE(disabled_collector->is_healthy());
181}
182
183TEST_F(ProcessMetricsCollectorTest, ConfigConstructor) {
184 process_metrics_config config;
185 config.collect_fd = true;
186 config.collect_inodes = false;
187 config.collect_context_switches = false;
188 config.fd_warning_threshold = 75.0;
189
190 auto collector = std::make_unique<process_metrics_collector>(config);
191 std::unordered_map<std::string, std::string> init_config;
192 collector->initialize(init_config);
193
194 auto stats = collector->get_statistics();
195 EXPECT_DOUBLE_EQ(stats["collect_fd"], 1.0);
196 EXPECT_DOUBLE_EQ(stats["collect_inodes"], 0.0);
197 EXPECT_DOUBLE_EQ(stats["collect_context_switches"], 0.0);
198 EXPECT_DOUBLE_EQ(stats["fd_warning_threshold"], 75.0);
199}
200
201TEST(ProcessMetricsStructTest, DefaultInitialization) {
202 process_metrics metrics;
203 EXPECT_EQ(metrics.fd.fd_used_process, 0);
204 EXPECT_EQ(metrics.inodes.total_inodes, 0);
205 EXPECT_EQ(metrics.context_switches.system_context_switches_total, 0);
206}
207
208TEST(FdInfoCollectorTest, BasicFunctionality) {
209 fd_info_collector collector;
210
211 EXPECT_NO_THROW(collector.is_fd_monitoring_available());
212
213 auto metrics = collector.collect_metrics();
214
215 auto now = std::chrono::system_clock::now();
216 auto diff = std::chrono::duration_cast<std::chrono::seconds>(now - metrics.timestamp);
217 EXPECT_LT(diff.count(), 10);
218}
219
220TEST(FdInfoCollectorTest, ProcessFDCountChangesWithOpenFiles) {
221 fd_info_collector collector;
222
223 auto initial = collector.collect_metrics();
224
225 std::vector<std::unique_ptr<std::fstream>> files;
226 for (int i = 0; i < 5; ++i) {
227 auto file = std::make_unique<std::fstream>();
228 file->open("/dev/null", std::ios::in);
229 if (file->is_open()) {
230 files.push_back(std::move(file));
231 }
232 }
233
234 auto after_open = collector.collect_metrics();
235
236 files.clear();
237
238 if (initial.fd_used_process > 0 && after_open.fd_used_process > 0) {
239 EXPECT_GE(after_open.fd_used_process, initial.fd_used_process);
240 }
241}
242
243TEST(InodeInfoCollectorTest, BasicFunctionality) {
244 inode_info_collector collector;
245
246 EXPECT_NO_THROW(collector.is_inode_monitoring_available());
247
248 auto metrics = collector.collect_metrics();
249
250 auto now = std::chrono::system_clock::now();
251 auto diff = std::chrono::duration_cast<std::chrono::seconds>(now - metrics.timestamp);
252 EXPECT_LT(diff.count(), 10);
253}
254
255TEST(ContextSwitchInfoCollectorTest, BasicFunctionality) {
256 context_switch_info_collector collector;
257
258 EXPECT_NO_THROW(collector.is_context_switch_monitoring_available());
259
260 auto metrics = collector.collect_metrics();
261
262 auto now = std::chrono::system_clock::now();
263 auto diff = std::chrono::duration_cast<std::chrono::seconds>(now - metrics.timestamp);
264 EXPECT_LT(diff.count(), 10);
265}
266
267#if defined(__linux__) || defined(__APPLE__)
268TEST_F(ProcessMetricsCollectorTest, UnixFdMonitoringAvailable) {
269 EXPECT_TRUE(collector_->is_fd_monitoring_available());
270}
271
272TEST_F(ProcessMetricsCollectorTest, UnixInodeMonitoringAvailable) {
273 EXPECT_TRUE(collector_->is_inode_monitoring_available());
274}
275
276TEST_F(ProcessMetricsCollectorTest, UnixContextSwitchMonitoringAvailable) {
277 EXPECT_TRUE(collector_->is_context_switch_monitoring_available());
278}
279
280TEST(InodeInfoCollectorTest, HasFilesystemsOnUnix) {
281 inode_info_collector collector;
282
283 if (collector.is_inode_monitoring_available()) {
284 auto metrics = collector.collect_metrics();
285 EXPECT_TRUE(metrics.metrics_available);
286 EXPECT_FALSE(metrics.filesystems.empty());
287 }
288}
289
290TEST(ContextSwitchInfoCollectorTest, ProcessSwitchesNonNegative) {
291 context_switch_info_collector collector;
292
293 if (collector.is_context_switch_monitoring_available()) {
294 auto metrics = collector.collect_metrics();
295 EXPECT_GE(metrics.process_info.voluntary_switches, 0);
296 EXPECT_GE(metrics.process_info.nonvoluntary_switches, 0);
297 EXPECT_GE(metrics.process_info.total_switches, 0);
298 }
299}
300#endif
301
302#if defined(_WIN32)
303TEST_F(ProcessMetricsCollectorTest, WindowsInodeMonitoringUnavailable) {
304 EXPECT_FALSE(collector_->is_inode_monitoring_available());
305}
306
307TEST_F(ProcessMetricsCollectorTest, WindowsContextSwitchMonitoringUnavailable) {
308 EXPECT_FALSE(collector_->is_context_switch_monitoring_available());
309}
310#endif
311
312} // namespace
313} // namespace monitoring
314} // namespace kcenon
Unified process-level metrics collector.
TEST(AdapterFunctionalityTest, WorksWithoutLogger)
Test Scenario 1: Adapter with NULL logger.
TEST_F(AdaptiveMonitoringTest, AdaptiveConfigDefaults)
std::unique_ptr< battery_collector > collector_