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 super::*;
16
17 use std::sync::LockResult;
18 use std::sync::MutexGuard;
19
20 use mockall::predicate::*;
21 use mockall::Sequence;
22
23 use crate::os::MockMeminfoApi;
24 use crate::os::MEMINFO_API_MTX;
25 use crate::zram::MockSysfsZramApi;
26 use crate::zram::ZRAM_API_MTX;
27
28 struct MockContext<'a> {
29 read_recomp_algorithm:
30 crate::zram::__mock_MockSysfsZramApi_SysfsZramApi::__read_recomp_algorithm::Context,
31 recompress: crate::zram::__mock_MockSysfsZramApi_SysfsZramApi::__recompress::Context,
32 set_idle: crate::zram::__mock_MockSysfsZramApi_SysfsZramApi::__set_idle::Context,
33 read_meminfo: crate::os::__mock_MockMeminfoApi_MeminfoApi::__read_meminfo::Context,
34 // Lock will be released after mock contexts are dropped.
35 _meminfo_lock: LockResult<MutexGuard<'a, ()>>,
36 _zram_lock: LockResult<MutexGuard<'a, ()>>,
37 }
38
39 impl<'a> MockContext<'a> {
new() -> Self40 fn new() -> Self {
41 let _zram_lock = ZRAM_API_MTX.lock();
42 let _meminfo_lock = MEMINFO_API_MTX.lock();
43 Self {
44 read_recomp_algorithm: MockSysfsZramApi::read_recomp_algorithm_context(),
45 recompress: MockSysfsZramApi::recompress_context(),
46 set_idle: MockSysfsZramApi::set_idle_context(),
47 read_meminfo: MockMeminfoApi::read_meminfo_context(),
48 _meminfo_lock,
49 _zram_lock,
50 }
51 }
52
setup_default_meminfo(&self)53 fn setup_default_meminfo(&self) {
54 let meminfo = "MemTotal: 8144296 kB
55 MemAvailable: 346452 kB";
56 self.read_meminfo.expect().returning(|| Ok(meminfo.to_string()));
57 }
58 }
59
60 #[test]
test_is_zram_recompression_activated()61 fn test_is_zram_recompression_activated() {
62 let mock = MockContext::new();
63 mock.read_recomp_algorithm.expect().returning(|| Ok("#1: lzo lzo-rle lz4 [zstd]".to_string()));
64
65 assert!(is_zram_recompression_activated::<MockSysfsZramApi>().unwrap());
66 }
67
68 #[test]
test_is_zram_recompression_activated_not_activated()69 fn test_is_zram_recompression_activated_not_activated() {
70 let mock = MockContext::new();
71 mock.read_recomp_algorithm.expect().returning(|| Ok("".to_string()));
72
73 assert!(!is_zram_recompression_activated::<MockSysfsZramApi>().unwrap());
74 }
75
76 #[test]
test_is_zram_recompression_activated_not_supported()77 fn test_is_zram_recompression_activated_not_supported() {
78 let mock = MockContext::new();
79 mock.read_recomp_algorithm
80 .expect()
81 .returning(|| Err(std::io::Error::new(std::io::ErrorKind::NotFound, "not found")));
82
83 assert!(!is_zram_recompression_activated::<MockSysfsZramApi>().unwrap());
84 }
85
86 #[test]
test_is_zram_recompression_activated_failure()87 fn test_is_zram_recompression_activated_failure() {
88 let mock = MockContext::new();
89 mock.read_recomp_algorithm.expect().returning(|| Err(std::io::Error::other("error")));
90
91 assert!(is_zram_recompression_activated::<MockSysfsZramApi>().is_err());
92 }
93
94 #[test]
mark_and_recompress()95 fn mark_and_recompress() {
96 let mock = MockContext::new();
97 let mut seq = Sequence::new();
98 mock.setup_default_meminfo();
99 let params = Params { max_mib: 0, ..Default::default() };
100 let mut zram_recompression = ZramRecompression::new();
101
102 mock.set_idle.expect().times(1).in_sequence(&mut seq).returning(|_| Ok(()));
103 mock.recompress
104 .expect()
105 .with(eq("type=huge_idle"))
106 .times(1)
107 .in_sequence(&mut seq)
108 .returning(|_| Ok(()));
109 mock.set_idle.expect().times(1).in_sequence(&mut seq).returning(|_| Ok(()));
110 mock.recompress
111 .expect()
112 .with(eq("type=idle"))
113 .times(1)
114 .in_sequence(&mut seq)
115 .returning(|_| Ok(()));
116 mock.recompress
117 .expect()
118 .with(eq("type=huge"))
119 .times(1)
120 .in_sequence(&mut seq)
121 .returning(|_| Ok(()));
122
123 assert!(zram_recompression
124 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, Instant::now())
125 .is_ok());
126 }
127
128 #[test]
mark_and_recompress_with_threshold()129 fn mark_and_recompress_with_threshold() {
130 let mock = MockContext::new();
131 mock.set_idle.expect().returning(|_| Ok(()));
132 mock.setup_default_meminfo();
133 let params = Params { max_mib: 12345, ..Default::default() };
134 let mut zram_recompression = ZramRecompression::new();
135
136 mock.recompress
137 .expect()
138 .with(eq("type=huge_idle threshold=12345"))
139 .times(1)
140 .returning(|_| Ok(()));
141 mock.recompress.expect().with(eq("type=idle threshold=12345")).times(1).returning(|_| Ok(()));
142 mock.recompress.expect().with(eq("type=huge threshold=12345")).times(1).returning(|_| Ok(()));
143
144 assert!(zram_recompression
145 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, Instant::now())
146 .is_ok());
147 }
148
149 #[test]
mark_and_recompress_before_backoff()150 fn mark_and_recompress_before_backoff() {
151 let mock = MockContext::new();
152 mock.recompress.expect().returning(|_| Ok(()));
153 mock.set_idle.expect().returning(|_| Ok(()));
154 mock.setup_default_meminfo();
155 let params =
156 Params { backoff_duration: Duration::from_secs(100), max_mib: 0, ..Default::default() };
157 let base_time = Instant::now();
158 let mut zram_recompression = ZramRecompression::new();
159 assert!(zram_recompression
160 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, base_time)
161 .is_ok());
162 mock.recompress.checkpoint();
163
164 mock.recompress.expect().times(0);
165
166 assert!(matches!(
167 zram_recompression.mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
168 ¶ms,
169 base_time + Duration::from_secs(99)
170 ),
171 Err(Error::BackoffTime)
172 ));
173 }
174
175 #[test]
mark_and_recompress_after_backoff()176 fn mark_and_recompress_after_backoff() {
177 let mock = MockContext::new();
178 mock.recompress.expect().returning(|_| Ok(()));
179 mock.set_idle.expect().returning(|_| Ok(()));
180 mock.setup_default_meminfo();
181 let params =
182 Params { backoff_duration: Duration::from_secs(100), max_mib: 0, ..Default::default() };
183 let base_time = Instant::now();
184 let mut zram_recompression = ZramRecompression::new();
185 assert!(zram_recompression
186 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, base_time)
187 .is_ok());
188 mock.recompress.checkpoint();
189 mock.set_idle.expect().returning(|_| Ok(()));
190 mock.setup_default_meminfo();
191
192 mock.recompress.expect().times(3).returning(|_| Ok(()));
193
194 assert!(zram_recompression
195 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(
196 ¶ms,
197 base_time + Duration::from_secs(100)
198 )
199 .is_ok());
200 }
201
202 #[test]
mark_and_recompress_idle_time()203 fn mark_and_recompress_idle_time() {
204 let mock = MockContext::new();
205 mock.recompress.expect().returning(|_| Ok(()));
206 let meminfo = "MemTotal: 10000 kB
207 MemAvailable: 8000 kB";
208 mock.read_meminfo.expect().returning(|| Ok(meminfo.to_string()));
209 let params = Params {
210 min_idle: Duration::from_secs(3600),
211 max_idle: Duration::from_secs(4000),
212 max_mib: 0,
213 ..Default::default()
214 };
215 let mut zram_recompression = ZramRecompression::new();
216
217 mock.set_idle.expect().with(eq("3747")).times(2).returning(|_| Ok(()));
218
219 assert!(zram_recompression
220 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, Instant::now())
221 .is_ok());
222 }
223
224 #[test]
mark_and_recompress_calculate_idle_failure()225 fn mark_and_recompress_calculate_idle_failure() {
226 let mock = MockContext::new();
227 mock.recompress.expect().returning(|_| Ok(()));
228 let params = Params {
229 min_idle: Duration::from_secs(4000),
230 max_idle: Duration::from_secs(3600),
231 max_mib: 0,
232 ..Default::default()
233 };
234 let mut zram_recompression = ZramRecompression::new();
235
236 assert!(matches!(
237 zram_recompression
238 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, Instant::now()),
239 Err(Error::CalculateIdle(_))
240 ));
241 }
242
243 #[test]
mark_and_recompress_mark_idle_failure()244 fn mark_and_recompress_mark_idle_failure() {
245 let mock = MockContext::new();
246 mock.setup_default_meminfo();
247 let params = Params { max_mib: 0, ..Default::default() };
248 let mut zram_recompression = ZramRecompression::new();
249
250 mock.set_idle.expect().returning(|_| Err(std::io::Error::other("error")));
251
252 assert!(matches!(
253 zram_recompression
254 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, Instant::now()),
255 Err(Error::MarkIdle(_))
256 ));
257 }
258
259 #[test]
mark_and_recompress_skip_huge_idle()260 fn mark_and_recompress_skip_huge_idle() {
261 let mock = MockContext::new();
262 mock.set_idle.expect().returning(|_| Ok(()));
263 mock.setup_default_meminfo();
264 let params = Params { huge_idle: false, max_mib: 0, ..Default::default() };
265 let mut zram_recompression = ZramRecompression::new();
266
267 mock.recompress.expect().with(eq("type=huge_idle")).times(0).returning(|_| Ok(()));
268 mock.recompress.expect().with(eq("type=idle")).times(1).returning(|_| Ok(()));
269 mock.recompress.expect().with(eq("type=huge")).times(1).returning(|_| Ok(()));
270
271 assert!(zram_recompression
272 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, Instant::now())
273 .is_ok());
274 }
275
276 #[test]
mark_and_recompress_skip_idle()277 fn mark_and_recompress_skip_idle() {
278 let mock = MockContext::new();
279 mock.set_idle.expect().returning(|_| Ok(()));
280 mock.setup_default_meminfo();
281 let params = Params { idle: false, max_mib: 0, ..Default::default() };
282 let mut zram_recompression = ZramRecompression::new();
283
284 mock.recompress.expect().with(eq("type=huge_idle")).times(1).returning(|_| Ok(()));
285 mock.recompress.expect().with(eq("type=idle")).times(0).returning(|_| Ok(()));
286 mock.recompress.expect().with(eq("type=huge")).times(1).returning(|_| Ok(()));
287
288 assert!(zram_recompression
289 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, Instant::now())
290 .is_ok());
291 }
292
293 #[test]
mark_and_recompress_skip_huge()294 fn mark_and_recompress_skip_huge() {
295 let mock = MockContext::new();
296 mock.set_idle.expect().returning(|_| Ok(()));
297 mock.setup_default_meminfo();
298 let params = Params { huge: false, max_mib: 0, ..Default::default() };
299 let mut zram_recompression = ZramRecompression::new();
300
301 mock.recompress.expect().with(eq("type=huge_idle")).times(1).returning(|_| Ok(()));
302 mock.recompress.expect().with(eq("type=idle")).times(1).returning(|_| Ok(()));
303 mock.recompress.expect().with(eq("type=huge")).times(0).returning(|_| Ok(()));
304
305 assert!(zram_recompression
306 .mark_and_recompress::<MockSysfsZramApi, MockMeminfoApi>(¶ms, Instant::now())
307 .is_ok());
308 }
309