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>(&params, 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>(&params, 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>(&params, 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             &params,
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>(&params, 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             &params,
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>(&params, 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>(&params, 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>(&params, 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>(&params, 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>(&params, 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>(&params, Instant::now())
307         .is_ok());
308 }
309