Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
test_resource_management.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>
6#include <thread>
7#include <chrono>
8#include <atomic>
10
11using namespace kcenon::monitoring;
12
13class ResourceManagementTest : public ::testing::Test {
14protected:
15 void SetUp() override {
16 call_count = 0;
17 success_count = 0;
18 }
19
20 void TearDown() override {
21 // Clean up any resources if needed
22 }
23
24 std::atomic<int> call_count{0};
25 std::atomic<int> success_count{0};
26
27 // Helper function for testing operations
28 kcenon::common::Result<int> test_operation() {
29 ++call_count;
31 return kcenon::common::ok(42);
32 }
33
34 // Helper function that simulates work
35 void simulate_work(std::chrono::milliseconds duration = std::chrono::milliseconds(10)) {
36 std::this_thread::sleep_for(duration);
37 }
38};
39
40// Token Bucket Rate Limiter Tests
41TEST_F(ResourceManagementTest, TokenBucketBasicOperation) {
42 auto limiter = create_token_bucket_limiter("test_limiter", 10, 10);
43
44 // Should be able to acquire tokens initially
45 EXPECT_TRUE(limiter->try_acquire(1));
46 EXPECT_TRUE(limiter->try_acquire(5));
47
48 // Should fail when bucket is empty
49 EXPECT_FALSE(limiter->try_acquire(10));
50}
51
52TEST_F(ResourceManagementTest, TokenBucketRefill) {
53 auto limiter = create_token_bucket_limiter("test_limiter", 100, 10);
54
55 // Exhaust all tokens
56 for (int i = 0; i < 10; ++i) {
57 EXPECT_TRUE(limiter->try_acquire(1));
58 }
59 EXPECT_FALSE(limiter->try_acquire(1));
60
61 // Wait for refill
62 std::this_thread::sleep_for(std::chrono::milliseconds(200));
63
64 // Should have tokens again
65 EXPECT_TRUE(limiter->try_acquire(1));
66}
67
68TEST_F(ResourceManagementTest, TokenBucketExecute) {
69 auto limiter = create_token_bucket_limiter("test_limiter", 100, 5,
70 throttling_strategy::reject);
71
72 // Execute operations within limit
73 for (int i = 0; i < 5; ++i) {
74 auto result = limiter->execute([this]() { return test_operation(); });
75 EXPECT_TRUE(result.is_ok());
76 EXPECT_EQ(result.value(), 42);
77 }
78
79 // Should reject when limit exceeded
80 auto result = limiter->execute([this]() { return test_operation(); });
81 EXPECT_TRUE(result.is_err());
82 EXPECT_EQ(static_cast<monitoring_error_code>(result.error().code), monitoring_error_code::resource_exhausted);
83}
84
85// Leaky Bucket Rate Limiter Tests
86TEST_F(ResourceManagementTest, LeakyBucketBasicOperation) {
87 auto limiter = create_leaky_bucket_limiter("test_limiter", 10, 10);
88
89 // Should be able to add items to bucket
90 EXPECT_TRUE(limiter->try_acquire(1));
91 EXPECT_TRUE(limiter->try_acquire(5));
92
93 // Should fail when bucket is full
94 EXPECT_FALSE(limiter->try_acquire(10));
95}
96
97TEST_F(ResourceManagementTest, LeakyBucketLeak) {
98 auto limiter = create_leaky_bucket_limiter("test_limiter", 100, 5);
99
100 // Fill bucket completely
101 for (int i = 0; i < 5; ++i) {
102 EXPECT_TRUE(limiter->try_acquire(1));
103 }
104 EXPECT_FALSE(limiter->try_acquire(1));
105
106 // Wait for leak
107 std::this_thread::sleep_for(std::chrono::milliseconds(100));
108
109 // Should have space again
110 EXPECT_TRUE(limiter->try_acquire(1));
111}
112
113// Memory Quota Manager Tests
114TEST_F(ResourceManagementTest, MemoryQuotaBasicAllocation) {
115 auto manager = create_memory_quota_manager("test_memory", 1024,
116 throttling_strategy::reject);
117
118 // Should allow allocation within quota
119 auto result1 = manager->allocate(512);
120 EXPECT_TRUE(result1.is_ok());
121 EXPECT_EQ(manager->current_usage(), 512);
122
123 // Should allow another allocation
124 auto result2 = manager->allocate(256);
125 EXPECT_TRUE(result2.is_ok());
126 EXPECT_EQ(manager->current_usage(), 768);
127
128 // Should reject when quota exceeded
129 auto result3 = manager->allocate(512);
130 EXPECT_TRUE(result3.is_err());
131 EXPECT_EQ(static_cast<monitoring_error_code>(result3.error().code), monitoring_error_code::resource_exhausted);
132}
133
134TEST_F(ResourceManagementTest, MemoryQuotaDeallocation) {
135 auto manager = create_memory_quota_manager("test_memory", 1024);
136
137 // Allocate memory
138 auto result = manager->allocate(512);
139 EXPECT_TRUE(result.is_ok());
140 EXPECT_EQ(manager->current_usage(), 512);
141
142 // Deallocate memory
143 manager->deallocate(256);
144 EXPECT_EQ(manager->current_usage(), 256);
145
146 // Should be able to allocate again
147 result = manager->allocate(512);
148 EXPECT_TRUE(result.is_ok());
149 EXPECT_EQ(manager->current_usage(), 768);
150}
151
152TEST_F(ResourceManagementTest, MemoryQuotaThresholds) {
153 resource_quota quota(resource_type::memory, 1000, throttling_strategy::reject);
154 quota.warning_threshold = 700;
155 quota.critical_threshold = 900;
156
157 auto manager = std::make_unique<memory_quota_manager>("test_memory", quota);
158
159 // Should not be over thresholds initially
160 EXPECT_FALSE(manager->is_over_warning_threshold());
161 EXPECT_FALSE(manager->is_over_critical_threshold());
162
163 // Allocate to warning level
164 manager->allocate(750);
165 EXPECT_TRUE(manager->is_over_warning_threshold());
166 EXPECT_FALSE(manager->is_over_critical_threshold());
167
168 // Allocate to critical level
169 manager->allocate(150);
170 EXPECT_TRUE(manager->is_over_warning_threshold());
171 EXPECT_TRUE(manager->is_over_critical_threshold());
172}
173
174TEST_F(ResourceManagementTest, MemoryQuotaMetrics) {
175 auto manager = create_memory_quota_manager("test_memory", 1024);
176
177 // Check initial metrics
178 auto metrics = manager->get_metrics();
179 EXPECT_EQ(metrics.current_usage.load(), 0);
180 EXPECT_EQ(metrics.total_allocations.load(), 0);
181
182 // Allocate and check metrics
183 manager->allocate(512);
184 metrics = manager->get_metrics();
185 EXPECT_EQ(metrics.current_usage.load(), 512);
186 EXPECT_EQ(metrics.total_allocations.load(), 1);
187 EXPECT_EQ(metrics.peak_usage.load(), 512);
188
189 // Allocate more and check peak
190 manager->allocate(256);
191 metrics = manager->get_metrics();
192 EXPECT_EQ(metrics.peak_usage.load(), 768);
193}
194
195// CPU Throttler Tests
196TEST_F(ResourceManagementTest, CPUThrottlerBasicOperation) {
197 cpu_throttle_config config;
198 config.max_cpu_usage = 0.8;
199 config.strategy = throttling_strategy::reject;
200 config.check_interval = std::chrono::milliseconds(10);
201
202 auto throttler = std::make_unique<cpu_throttler>("test_cpu", config);
203
204 // Should execute when CPU usage is low
205 auto result = throttler->execute([this]() { return test_operation(); });
206 EXPECT_TRUE(result.is_ok());
207 EXPECT_EQ(result.value(), 42);
208}
209
210TEST_F(ResourceManagementTest, CPUThrottlerMetrics) {
211 cpu_throttle_config config;
212 config.max_cpu_usage = 0.8;
213 config.strategy = throttling_strategy::delay;
214
215 auto throttler = std::make_unique<cpu_throttler>("test_cpu", config);
216
217 // Execute operation and check metrics
218 throttler->execute([this]() { return test_operation(); });
219
220 auto metrics = throttler->get_metrics();
221 EXPECT_EQ(metrics.total_allocations.load(), 1);
222}
223
224// Resource Manager Tests
225TEST_F(ResourceManagementTest, ResourceManagerRateLimiter) {
226 auto manager = create_resource_manager("test_manager");
227
228 rate_limit_config config;
229 config.rate_per_second = 100;
230 config.burst_capacity = 10;
231
232 auto result = manager->add_rate_limiter("api_limiter", config);
233 EXPECT_TRUE(result.is_ok());
234
235 auto limiter = manager->get_rate_limiter("api_limiter");
236 EXPECT_NE(limiter, nullptr);
237 EXPECT_EQ(limiter->get_name(), "api_limiter");
238}
239
240TEST_F(ResourceManagementTest, ResourceManagerMemoryQuota) {
241 auto manager = create_resource_manager("test_manager");
242
243 resource_quota quota(resource_type::memory, 2048, throttling_strategy::reject);
244
245 auto result = manager->add_memory_quota("memory_quota", quota);
246 EXPECT_TRUE(result.is_ok());
247
248 auto memory_manager = manager->get_memory_quota("memory_quota");
249 EXPECT_NE(memory_manager, nullptr);
250 EXPECT_EQ(memory_manager->get_name(), "memory_quota");
251}
252
253TEST_F(ResourceManagementTest, ResourceManagerCPUThrottler) {
254 auto manager = create_resource_manager("test_manager");
255
256 cpu_throttle_config config;
257 config.max_cpu_usage = 0.7;
258 config.strategy = throttling_strategy::delay;
259
260 auto result = manager->add_cpu_throttler("cpu_throttler", config);
261 EXPECT_TRUE(result.is_ok());
262
263 auto throttler = manager->get_cpu_throttler("cpu_throttler");
264 EXPECT_NE(throttler, nullptr);
265 EXPECT_EQ(throttler->get_name(), "cpu_throttler");
266}
267
268TEST_F(ResourceManagementTest, ResourceManagerDuplicateNames) {
269 auto manager = create_resource_manager("test_manager");
270
271 rate_limit_config config;
272 config.rate_per_second = 100;
273 config.burst_capacity = 10;
274
275 // First addition should succeed
276 auto result1 = manager->add_rate_limiter("duplicate_name", config);
277 EXPECT_TRUE(result1.is_ok());
278
279 // Second addition with same name should fail
280 auto result2 = manager->add_rate_limiter("duplicate_name", config);
281 EXPECT_TRUE(result2.is_err());
282 EXPECT_EQ(static_cast<monitoring_error_code>(result2.error().code), monitoring_error_code::already_exists);
283}
284
285TEST_F(ResourceManagementTest, ResourceManagerHealthCheck) {
286 auto manager = create_resource_manager("test_manager");
287
288 // Add components
289 resource_quota quota(resource_type::memory, 1024, throttling_strategy::reject);
290 manager->add_memory_quota("memory", quota);
291
292 cpu_throttle_config cpu_config;
293 cpu_config.max_cpu_usage = 0.8;
294 manager->add_cpu_throttler("cpu", cpu_config);
295
296 // Should be healthy initially
297 auto health = manager->is_healthy();
298 EXPECT_TRUE(health.is_ok());
299 EXPECT_TRUE(health.value());
300}
301
302TEST_F(ResourceManagementTest, ResourceManagerMetrics) {
303 auto manager = create_resource_manager("test_manager");
304
305 // Add rate limiter
306 rate_limit_config rate_config;
307 rate_config.rate_per_second = 100;
308 rate_config.burst_capacity = 10;
309 manager->add_rate_limiter("rate", rate_config);
310
311 // Add memory quota
312 resource_quota quota(resource_type::memory, 1024, throttling_strategy::reject);
313 manager->add_memory_quota("memory", quota);
314
315 // Get all metrics
316 auto all_metrics = manager->get_all_metrics();
317 EXPECT_EQ(all_metrics.size(), 2);
318 EXPECT_TRUE(all_metrics.find("rate_rate") != all_metrics.end());
319 EXPECT_TRUE(all_metrics.find("memory_memory") != all_metrics.end());
320}
321
322// Configuration Validation Tests
323TEST_F(ResourceManagementTest, RateLimitConfigValidation) {
324 rate_limit_config config;
325
326 // Valid config
327 config.rate_per_second = 100;
328 config.burst_capacity = 10;
329 EXPECT_TRUE(config.validate());
330
331 // Invalid rate
332 config.rate_per_second = 0;
333 EXPECT_FALSE(config.validate());
334
335 // Invalid burst capacity
336 config.rate_per_second = 100;
337 config.burst_capacity = 0;
338 EXPECT_FALSE(config.validate());
339}
340
341TEST_F(ResourceManagementTest, ResourceQuotaValidation) {
342 resource_quota quota;
343
344 // Valid quota
345 quota.type = resource_type::memory;
346 quota.max_value = 1024;
347 quota.warning_threshold = 700;
348 quota.critical_threshold = 900;
349 EXPECT_TRUE(quota.validate());
350
351 // Invalid max value
352 quota.max_value = 0;
353 EXPECT_FALSE(quota.validate());
354
355 // Invalid thresholds
356 quota.max_value = 1024;
357 quota.warning_threshold = 1100;
358 EXPECT_FALSE(quota.validate());
359
360 quota.warning_threshold = 700;
361 quota.critical_threshold = 600;
362 EXPECT_FALSE(quota.validate());
363}
364
365TEST_F(ResourceManagementTest, CPUThrottleConfigValidation) {
366 cpu_throttle_config config;
367
368 // Valid config
369 config.max_cpu_usage = 0.8;
370 config.warning_threshold = 0.7;
371 EXPECT_TRUE(config.validate());
372
373 // Invalid max CPU usage
374 config.max_cpu_usage = 0.0;
375 EXPECT_FALSE(config.validate());
376
377 config.max_cpu_usage = 1.5;
378 EXPECT_FALSE(config.validate());
379
380 // Invalid warning threshold
381 config.max_cpu_usage = 0.8;
382 config.warning_threshold = 0.9;
383 EXPECT_FALSE(config.validate());
384}
385
386// Concurrency Tests
387TEST_F(ResourceManagementTest, RateLimiterConcurrency) {
388 auto limiter = create_token_bucket_limiter("concurrent_test", 1000, 100,
389 throttling_strategy::reject);
390
391 const int num_threads = 10;
392 const int operations_per_thread = 10;
393 std::atomic<int> successful_operations{0};
394
395 std::vector<std::thread> threads;
396 for (int i = 0; i < num_threads; ++i) {
397 threads.emplace_back([&]() {
398 for (int j = 0; j < operations_per_thread; ++j) {
399 if (limiter->try_acquire(1)) {
400 successful_operations++;
401 }
402 }
403 });
404 }
405
406 for (auto& thread : threads) {
407 thread.join();
408 }
409
410 // Should have some successful operations
411 EXPECT_GT(successful_operations.load(), 0);
412 EXPECT_LE(successful_operations.load(), 100); // Limited by burst capacity
413}
414
415TEST_F(ResourceManagementTest, MemoryQuotaConcurrency) {
416 auto manager = create_memory_quota_manager("concurrent_memory", 10000,
417 throttling_strategy::reject);
418
419 const int num_threads = 5;
420 const int allocations_per_thread = 10;
421 std::atomic<int> successful_allocations{0};
422
423 std::vector<std::thread> threads;
424 for (int i = 0; i < num_threads; ++i) {
425 threads.emplace_back([&]() {
426 for (int j = 0; j < allocations_per_thread; ++j) {
427 if (manager->allocate(500).is_ok()) {
428 successful_allocations++;
429 // Simulate some work then deallocate
430 simulate_work(std::chrono::milliseconds(1));
431 manager->deallocate(500);
432 }
433 }
434 });
435 }
436
437 for (auto& thread : threads) {
438 thread.join();
439 }
440
441 EXPECT_GT(successful_allocations.load(), 0);
442 EXPECT_EQ(manager->current_usage(), 0); // All should be deallocated
443}
444
445// Performance Tests
446TEST_F(ResourceManagementTest, RateLimiterPerformance) {
447 auto limiter = create_token_bucket_limiter("perf_test", 10000, 1000);
448
449 const int num_operations = 1000;
450 auto start = std::chrono::steady_clock::now();
451
452 for (int i = 0; i < num_operations; ++i) {
453 limiter->try_acquire(1);
454 }
455
456 auto end = std::chrono::steady_clock::now();
457 auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
458 end - start);
459
460 // Should complete operations reasonably quickly
461 EXPECT_LT(duration.count(), 100000); // Less than 100ms
462}
463
464TEST_F(ResourceManagementTest, MemoryQuotaPerformance) {
465 auto manager = create_memory_quota_manager("perf_memory", 1000000);
466
467 const int num_operations = 1000;
468 auto start = std::chrono::steady_clock::now();
469
470 for (int i = 0; i < num_operations; ++i) {
471 manager->allocate(100);
472 manager->deallocate(100);
473 }
474
475 auto end = std::chrono::steady_clock::now();
476 auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
477 end - start);
478
479 // Should complete operations reasonably quickly
480 EXPECT_LT(duration.count(), 50000); // Less than 50ms
481}
kcenon::common::Result< int > test_operation()
void simulate_work(std::chrono::milliseconds duration=std::chrono::milliseconds(10))
std::unique_ptr< memory_quota_manager > create_memory_quota_manager(const std::string &name, size_t max_bytes, throttling_strategy strategy=throttling_strategy::reject)
Create a memory quota manager.
std::unique_ptr< token_bucket_limiter > create_token_bucket_limiter(const std::string &name, double rate, size_t capacity, throttling_strategy strategy=throttling_strategy::reject)
Create a token bucket rate limiter.
std::unique_ptr< resource_manager > create_resource_manager(const std::string &name)
Create a resource manager.
monitoring_error_code
Comprehensive error codes for monitoring system operations.
Definition error_codes.h:25
std::unique_ptr< leaky_bucket_limiter > create_leaky_bucket_limiter(const std::string &name, double rate, size_t capacity)
Create a leaky bucket rate limiter.
Resource exhaustion handling strategies and limits.
Configuration for CPU throttling.
bool validate() const
Validate configuration.
double max_cpu_usage
Maximum CPU usage (0.0 to 1.0)
double warning_threshold
Warning threshold (0.0 to 1.0)
Configuration for rate limiting.
size_t burst_capacity
Maximum burst capacity.
double rate_per_second
Rate of token refill per second.
bool validate() const
Validate configuration.
Configuration for resource quotas.
size_t critical_threshold
Critical level threshold.
size_t max_value
Maximum allowed resource usage.
size_t warning_threshold
Warning level threshold.
bool validate() const
Validate configuration.
TEST_F(ResourceManagementTest, TokenBucketBasicOperation)