1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use std::sync::LockResult;
16 use std::sync::MutexGuard;
17
18 use mockall::predicate::*;
19 use mockall::Sequence;
20
21 use super::*;
22 use crate::os::MockMeminfoApi;
23 use crate::os::MEMINFO_API_MTX;
24 use crate::time::TimeApi;
25 use crate::time::TimeApiImpl;
26 use crate::zram::MockSysfsZramApi;
27 use crate::zram::ZRAM_API_MTX;
28
29 struct MockContext<'a> {
30 read_recomp_algorithm:
31 crate::zram::__mock_MockSysfsZramApi_SysfsZramApi::__read_recomp_algorithm::Context,
32 recompress: crate::zram::__mock_MockSysfsZramApi_SysfsZramApi::__recompress::Context,
33 set_idle: crate::zram::__mock_MockSysfsZramApi_SysfsZramApi::__set_idle::Context,
34 read_meminfo: crate::os::__mock_MockMeminfoApi_MeminfoApi::__read_meminfo::Context,
35 // Lock will be released after mock contexts are dropped.
36 _meminfo_lock: LockResult<MutexGuard<'a, ()>>,
37 _zram_lock: LockResult<MutexGuard<'a, ()>>,
38 }
39
40 impl MockContext<'_> {
new() -> Self41 fn new() -> Self {
42 let _zram_lock = ZRAM_API_MTX.lock();
43 let _meminfo_lock = MEMINFO_API_MTX.lock();
44 Self {
45 read_recomp_algorithm: MockSysfsZramApi::read_recomp_algorithm_context(),
46 recompress: MockSysfsZramApi::recompress_context(),
47 set_idle: MockSysfsZramApi::set_idle_context(),
48 read_meminfo: MockMeminfoApi::read_meminfo_context(),
49 _meminfo_lock,
50 _zram_lock,
51 }
52 }
53
setup_default_meminfo(&self)54 fn setup_default_meminfo(&self) {
55 let meminfo = "MemTotal: 8144296 kB
56 MemAvailable: 346452 kB";
57 self.read_meminfo.expect().returning(|| Ok(meminfo.to_string()));
58 }
59 }
60
61 #[test]
get_zram_recompression_status_not_configured()62 fn get_zram_recompression_status_not_configured() {
63 let mock = MockContext::new();
64 mock.read_recomp_algorithm.expect().returning(|| Ok("".to_string()));
65 assert_eq!(
66 get_zram_recompression_status::<MockSysfsZramApi>().unwrap(),
67 ZramRecompressionStatus::NotConfigured
68 );
69 }
70
71 #[test]
get_zram_recompression_status_activated()72 fn get_zram_recompression_status_activated() {
73 let mock = MockContext::new();
74 mock.read_recomp_algorithm.expect().returning(|| Ok("zstd".to_string()));
75 assert_eq!(
76 get_zram_recompression_status::<MockSysfsZramApi>().unwrap(),
77 ZramRecompressionStatus::Activated
78 );
79 }
80
81 #[test]
get_zram_recompression_status_unsupported()82 fn get_zram_recompression_status_unsupported() {
83 let mock = MockContext::new();
84 mock.read_recomp_algorithm
85 .expect()
86 .returning(|| Err(std::io::Error::from_raw_os_error(libc::ENOENT)));
87 assert_eq!(
88 get_zram_recompression_status::<MockSysfsZramApi>().unwrap(),
89 ZramRecompressionStatus::Unsupported
90 );
91 }
92
93 #[test]
test_get_zram_recompression_status_failure()94 fn test_get_zram_recompression_status_failure() {
95 let mock = MockContext::new();
96 mock.read_recomp_algorithm.expect().returning(|| Err(std::io::Error::other("error")));
97
98 assert!(get_zram_recompression_status::<MockSysfsZramApi>().is_err());
99 }
100
101 #[test]
mark_and_recompress()102 fn mark_and_recompress() {
103 let mock = MockContext::new();
104 let mut seq = Sequence::new();
105 mock.setup_default_meminfo();
106 let params = Params { threshold_bytes: 0, ..Default::default() };
107 let suspend_history = SuspendHistory::new();
108 let mut zram_recompression = ZramRecompression::new();
109
110 mock.set_idle.expect().times(1).in_sequence(&mut seq).returning(|_| Ok(()));
111 mock.recompress
112 .expect()
113 .with(eq("type=huge_idle"))
114 .times(1)
115 .in_sequence(&mut seq)
116 .returning(|_| Ok(()));
117 mock.set_idle.expect().times(1).in_sequence(&mut seq).returning(|_| Ok(()));
118 mock.recompress
119 .expect()
120 .with(eq("type=idle"))
121 .times(1)
122 .in_sequence(&mut seq)
123 .returning(|_| Ok(()));
124 mock.recompress
125 .expect()
126 .with(eq("type=huge"))
127 .times(1)
128 .in_sequence(&mut seq)
129 .returning(|_| Ok(()));
130
131 assert!(zram_recompression
132 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
133 ¶ms,
134 &suspend_history,
135 TimeApiImpl::get_boot_time()
136 )
137 .is_ok());
138 }
139
140 #[test]
mark_and_recompress_with_threshold()141 fn mark_and_recompress_with_threshold() {
142 let mock = MockContext::new();
143 mock.set_idle.expect().returning(|_| Ok(()));
144 mock.setup_default_meminfo();
145 let params = Params { threshold_bytes: 12345, ..Default::default() };
146 let suspend_history = SuspendHistory::new();
147 let mut zram_recompression = ZramRecompression::new();
148
149 mock.recompress
150 .expect()
151 .with(eq("type=huge_idle threshold=12345"))
152 .times(1)
153 .returning(|_| Ok(()));
154 mock.recompress.expect().with(eq("type=idle threshold=12345")).times(1).returning(|_| Ok(()));
155 mock.recompress.expect().with(eq("type=huge threshold=12345")).times(1).returning(|_| Ok(()));
156
157 assert!(zram_recompression
158 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
159 ¶ms,
160 &suspend_history,
161 TimeApiImpl::get_boot_time(),
162 )
163 .is_ok());
164 }
165
166 #[test]
mark_and_recompress_before_backoff()167 fn mark_and_recompress_before_backoff() {
168 let mock = MockContext::new();
169 mock.recompress.expect().returning(|_| Ok(()));
170 mock.set_idle.expect().returning(|_| Ok(()));
171 mock.setup_default_meminfo();
172 let params = Params {
173 backoff_duration: Duration::from_secs(100),
174 threshold_bytes: 0,
175 ..Default::default()
176 };
177 let suspend_history = SuspendHistory::new();
178 let base_time = TimeApiImpl::get_boot_time();
179 let mut zram_recompression = ZramRecompression::new();
180 assert!(zram_recompression
181 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
182 ¶ms,
183 &suspend_history,
184 base_time,
185 )
186 .is_ok());
187 mock.recompress.checkpoint();
188
189 mock.recompress.expect().times(0);
190
191 assert!(matches!(
192 zram_recompression.mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
193 ¶ms,
194 &suspend_history,
195 base_time.checked_add(Duration::from_secs(99)).unwrap(),
196 ),
197 Err(Error::BackoffTime)
198 ));
199 }
200
201 #[test]
mark_and_recompress_after_backoff()202 fn mark_and_recompress_after_backoff() {
203 let mock = MockContext::new();
204 mock.recompress.expect().returning(|_| Ok(()));
205 mock.set_idle.expect().returning(|_| Ok(()));
206 mock.setup_default_meminfo();
207 let params = Params {
208 backoff_duration: Duration::from_secs(100),
209 threshold_bytes: 0,
210 ..Default::default()
211 };
212 let suspend_history = SuspendHistory::new();
213 let base_time = TimeApiImpl::get_boot_time();
214 let mut zram_recompression = ZramRecompression::new();
215 assert!(zram_recompression
216 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
217 ¶ms,
218 &suspend_history,
219 base_time,
220 )
221 .is_ok());
222 mock.recompress.checkpoint();
223 mock.set_idle.expect().returning(|_| Ok(()));
224 mock.setup_default_meminfo();
225
226 mock.recompress.expect().times(3).returning(|_| Ok(()));
227
228 assert!(zram_recompression
229 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
230 ¶ms,
231 &suspend_history,
232 base_time.checked_add(Duration::from_secs(100)).unwrap()
233 )
234 .is_ok());
235 }
236
237 #[test]
mark_and_recompress_idle_time()238 fn mark_and_recompress_idle_time() {
239 let mock = MockContext::new();
240 mock.recompress.expect().returning(|_| Ok(()));
241 let meminfo = "MemTotal: 10000 kB
242 MemAvailable: 8000 kB";
243 mock.read_meminfo.expect().returning(|| Ok(meminfo.to_string()));
244 let params = Params {
245 min_idle: Duration::from_secs(3600),
246 max_idle: Duration::from_secs(4000),
247 threshold_bytes: 0,
248 ..Default::default()
249 };
250 let suspend_history = SuspendHistory::new();
251 let mut zram_recompression = ZramRecompression::new();
252
253 mock.set_idle.expect().with(eq("3747")).times(2).returning(|_| Ok(()));
254
255 assert!(zram_recompression
256 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
257 ¶ms,
258 &suspend_history,
259 TimeApiImpl::get_boot_time()
260 )
261 .is_ok());
262 }
263
264 #[test]
mark_and_recompress_idle_time_adjusted_by_suspend_duration()265 fn mark_and_recompress_idle_time_adjusted_by_suspend_duration() {
266 let mock = MockContext::new();
267 mock.recompress.expect().returning(|_| Ok(()));
268 let meminfo = "MemTotal: 10000 kB
269 MemAvailable: 8000 kB";
270 mock.read_meminfo.expect().returning(|| Ok(meminfo.to_string()));
271 let params = Params {
272 min_idle: Duration::from_secs(3600),
273 max_idle: Duration::from_secs(4000),
274 threshold_bytes: 0,
275 ..Default::default()
276 };
277 let mut suspend_history = SuspendHistory::new();
278 let boot_now = BootTime::from_duration(Duration::from_secs(12345));
279 suspend_history.record_suspend_duration(Duration::from_secs(1000), boot_now, params.max_idle);
280 let mut zram_recompression = ZramRecompression::new();
281
282 mock.set_idle.expect().with(eq("4747")).times(2).returning(|_| Ok(()));
283
284 assert!(zram_recompression
285 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
286 ¶ms,
287 &suspend_history,
288 boot_now
289 )
290 .is_ok());
291 }
292
293 #[test]
mark_and_recompress_calculate_idle_failure()294 fn mark_and_recompress_calculate_idle_failure() {
295 let mock = MockContext::new();
296 mock.recompress.expect().returning(|_| Ok(()));
297 let params = Params {
298 min_idle: Duration::from_secs(4000),
299 max_idle: Duration::from_secs(3600),
300 threshold_bytes: 0,
301 ..Default::default()
302 };
303 let suspend_history = SuspendHistory::new();
304 let mut zram_recompression = ZramRecompression::new();
305
306 assert!(matches!(
307 zram_recompression.mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
308 ¶ms,
309 &suspend_history,
310 TimeApiImpl::get_boot_time()
311 ),
312 Err(Error::CalculateIdle(_))
313 ));
314 }
315
316 #[test]
mark_and_recompress_mark_idle_failure()317 fn mark_and_recompress_mark_idle_failure() {
318 let mock = MockContext::new();
319 mock.setup_default_meminfo();
320 let params = Params { threshold_bytes: 0, ..Default::default() };
321 let suspend_history = SuspendHistory::new();
322 let mut zram_recompression = ZramRecompression::new();
323
324 mock.set_idle.expect().returning(|_| Err(std::io::Error::other("error")));
325
326 assert!(matches!(
327 zram_recompression.mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
328 ¶ms,
329 &suspend_history,
330 TimeApiImpl::get_boot_time()
331 ),
332 Err(Error::MarkIdle(_))
333 ));
334 }
335
336 #[test]
mark_and_recompress_skip_huge_idle()337 fn mark_and_recompress_skip_huge_idle() {
338 let mock = MockContext::new();
339 mock.set_idle.expect().returning(|_| Ok(()));
340 mock.setup_default_meminfo();
341 let params = Params { huge_idle: false, threshold_bytes: 0, ..Default::default() };
342 let suspend_history = SuspendHistory::new();
343 let mut zram_recompression = ZramRecompression::new();
344
345 mock.recompress.expect().with(eq("type=huge_idle")).times(0).returning(|_| Ok(()));
346 mock.recompress.expect().with(eq("type=idle")).times(1).returning(|_| Ok(()));
347 mock.recompress.expect().with(eq("type=huge")).times(1).returning(|_| Ok(()));
348
349 assert!(zram_recompression
350 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
351 ¶ms,
352 &suspend_history,
353 TimeApiImpl::get_boot_time()
354 )
355 .is_ok());
356 }
357
358 #[test]
mark_and_recompress_skip_idle()359 fn mark_and_recompress_skip_idle() {
360 let mock = MockContext::new();
361 mock.set_idle.expect().returning(|_| Ok(()));
362 mock.setup_default_meminfo();
363 let params = Params { idle: false, threshold_bytes: 0, ..Default::default() };
364 let suspend_history = SuspendHistory::new();
365 let mut zram_recompression = ZramRecompression::new();
366
367 mock.recompress.expect().with(eq("type=huge_idle")).times(1).returning(|_| Ok(()));
368 mock.recompress.expect().with(eq("type=idle")).times(0).returning(|_| Ok(()));
369 mock.recompress.expect().with(eq("type=huge")).times(1).returning(|_| Ok(()));
370
371 assert!(zram_recompression
372 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
373 ¶ms,
374 &suspend_history,
375 TimeApiImpl::get_boot_time()
376 )
377 .is_ok());
378 }
379
380 #[test]
mark_and_recompress_skip_huge()381 fn mark_and_recompress_skip_huge() {
382 let mock = MockContext::new();
383 mock.set_idle.expect().returning(|_| Ok(()));
384 mock.setup_default_meminfo();
385 let params = Params { huge: false, threshold_bytes: 0, ..Default::default() };
386 let suspend_history = SuspendHistory::new();
387 let mut zram_recompression = ZramRecompression::new();
388
389 mock.recompress.expect().with(eq("type=huge_idle")).times(1).returning(|_| Ok(()));
390 mock.recompress.expect().with(eq("type=idle")).times(1).returning(|_| Ok(()));
391 mock.recompress.expect().with(eq("type=huge")).times(0).returning(|_| Ok(()));
392
393 assert!(zram_recompression
394 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
395 ¶ms,
396 &suspend_history,
397 TimeApiImpl::get_boot_time()
398 )
399 .is_ok());
400 }
401