cli_testing_specialist/utils/
resource_limits.rs1use crate::error::{CliTestError, Result};
2use std::time::Duration;
3
4#[derive(Debug, Clone)]
12pub struct ResourceLimits {
13 pub max_memory_bytes: u64,
15
16 pub max_file_descriptors: u64,
18
19 pub max_processes: u64,
21
22 pub execution_timeout: Duration,
24}
25
26impl Default for ResourceLimits {
27 fn default() -> Self {
28 Self {
29 max_memory_bytes: 500 * 1024 * 1024, max_file_descriptors: 1024,
31 max_processes: 100,
32 execution_timeout: Duration::from_secs(300), }
34 }
35}
36
37impl ResourceLimits {
38 pub fn new(
40 max_memory_bytes: u64,
41 max_file_descriptors: u64,
42 max_processes: u64,
43 execution_timeout: Duration,
44 ) -> Self {
45 Self {
46 max_memory_bytes,
47 max_file_descriptors,
48 max_processes,
49 execution_timeout,
50 }
51 }
52
53 #[cfg(unix)]
58 pub fn apply(&self) -> Result<()> {
59 use libc::{rlimit, setrlimit, RLIMIT_AS, RLIMIT_NOFILE, RLIMIT_NPROC};
60
61 let mem_limit = rlimit {
63 rlim_cur: self.max_memory_bytes,
64 rlim_max: self.max_memory_bytes,
65 };
66
67 unsafe {
68 if setrlimit(RLIMIT_AS, &mem_limit) != 0 {
69 return Err(CliTestError::ExecutionFailed(
70 "Failed to set memory limit".to_string(),
71 ));
72 }
73 }
74
75 let fd_limit = rlimit {
77 rlim_cur: self.max_file_descriptors,
78 rlim_max: self.max_file_descriptors,
79 };
80
81 unsafe {
82 if setrlimit(RLIMIT_NOFILE, &fd_limit) != 0 {
83 return Err(CliTestError::ExecutionFailed(
84 "Failed to set file descriptor limit".to_string(),
85 ));
86 }
87 }
88
89 let proc_limit = rlimit {
91 rlim_cur: self.max_processes,
92 rlim_max: self.max_processes,
93 };
94
95 unsafe {
96 if setrlimit(RLIMIT_NPROC, &proc_limit) != 0 {
97 return Err(CliTestError::ExecutionFailed(
98 "Failed to set process limit".to_string(),
99 ));
100 }
101 }
102
103 Ok(())
104 }
105
106 #[cfg(not(unix))]
111 pub fn apply(&self) -> Result<()> {
112 log::warn!("Resource limits not supported on this platform");
113 Ok(())
114 }
115
116 pub fn timeout(&self) -> Duration {
118 self.execution_timeout
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_default_limits() {
128 let limits = ResourceLimits::default();
129
130 assert_eq!(limits.max_memory_bytes, 500 * 1024 * 1024);
131 assert_eq!(limits.max_file_descriptors, 1024);
132 assert_eq!(limits.max_processes, 100);
133 assert_eq!(limits.execution_timeout, Duration::from_secs(300));
134 }
135
136 #[test]
137 fn test_custom_limits() {
138 let limits = ResourceLimits::new(100 * 1024 * 1024, 512, 50, Duration::from_secs(60));
139
140 assert_eq!(limits.max_memory_bytes, 100 * 1024 * 1024);
141 assert_eq!(limits.max_file_descriptors, 512);
142 assert_eq!(limits.max_processes, 50);
143 assert_eq!(limits.execution_timeout, Duration::from_secs(60));
144 }
145
146 #[test]
147 fn test_timeout_accessor() {
148 let limits = ResourceLimits::default();
149 assert_eq!(limits.timeout(), Duration::from_secs(300));
150 }
151
152 #[cfg(unix)]
153 #[test]
154 fn test_apply_limits_unix() {
155 let limits = ResourceLimits::new(100 * 1024 * 1024, 512, 50, Duration::from_secs(60));
158
159 let _ = limits.apply();
161 }
162}