1 use super::CacheConfig;
2 use std::fs;
3 use std::path::PathBuf;
4 use std::time::Duration;
5 use tempfile::TempDir;
6 
7 // note: config loading during validation creates cache directory to canonicalize its path,
8 //       that's why these function and macro always use custom cache directory
9 // note: tempdir removes directory when being dropped, so we need to return it to the caller,
10 //       so the paths are valid
11 pub fn test_prolog() -> (TempDir, PathBuf, PathBuf) {
12     let _ = pretty_env_logger::try_init();
13     let temp_dir = tempfile::tempdir().expect("Can't create temporary directory");
14     let cache_dir = temp_dir.path().join("cache-dir");
15     let config_path = temp_dir.path().join("cache-config.toml");
16     (temp_dir, cache_dir, config_path)
17 }
18 
19 macro_rules! load_config {
20     ($config_path:ident, $content_fmt:expr, $cache_dir:ident) => {{
21         let config_path = &$config_path;
22         let content = format!($content_fmt, cache_dir = $cache_dir.display());
23         fs::write(config_path, content).expect("Failed to write test config file");
24         CacheConfig::from_file(Some(config_path)).unwrap()
25     }};
26 }
27 
28 macro_rules! bad_config {
29     ($config_path:ident, $content_fmt:expr, $cache_dir:ident) => {{
30         let config_path = &$config_path;
31         let content = format!($content_fmt, cache_dir = $cache_dir.display());
32         fs::write(config_path, content).expect("Failed to write test config file");
33         assert!(CacheConfig::from_file(Some(config_path)).is_err());
34     }};
35 }
36 
37 // test without macros to test being disabled
38 #[test]
39 fn test_disabled() {
40     let dir = tempfile::tempdir().expect("Can't create temporary directory");
41     let config_path = dir.path().join("cache-config.toml");
42     let config_content = "[cache]\n\
43                           enabled = false\n";
44     fs::write(&config_path, config_content).expect("Failed to write test config file");
45     let conf = CacheConfig::from_file(Some(&config_path)).unwrap();
46     assert!(!conf.enabled());
47 }
48 
49 #[test]
50 fn test_unrecognized_settings() {
51     let (_td, cd, cp) = test_prolog();
52     bad_config!(
53         cp,
54         "unrecognized-setting = 42\n\
55          [cache]\n\
56          enabled = true\n\
57          directory = '{cache_dir}'",
58         cd
59     );
60 
61     bad_config!(
62         cp,
63         "[cache]\n\
64          enabled = true\n\
65          directory = '{cache_dir}'\n\
66          unrecognized-setting = 42",
67         cd
68     );
69 }
70 
71 #[test]
72 fn test_all_settings() {
73     let (_td, cd, cp) = test_prolog();
74     let conf = load_config!(
75         cp,
76         "[cache]\n\
77          enabled = true\n\
78          directory = '{cache_dir}'\n\
79          worker-event-queue-size = '16'\n\
80          baseline-compression-level = 3\n\
81          optimized-compression-level = 20\n\
82          optimized-compression-usage-counter-threshold = '256'\n\
83          cleanup-interval = '1h'\n\
84          optimizing-compression-task-timeout = '30m'\n\
85          allowed-clock-drift-for-files-from-future = '1d'\n\
86          file-count-soft-limit = '65536'\n\
87          files-total-size-soft-limit = '512Mi'\n\
88          file-count-limit-percent-if-deleting = '70%'\n\
89          files-total-size-limit-percent-if-deleting = '70%'",
90         cd
91     );
92     check_conf(&conf, &cd);
93 
94     let conf = load_config!(
95         cp,
96         // added some white spaces
97         "[cache]\n\
98          enabled = true\n\
99          directory = '{cache_dir}'\n\
100          worker-event-queue-size  = ' 16\t'\n\
101          baseline-compression-level = 3\n\
102          optimized-compression-level =\t 20\n\
103          optimized-compression-usage-counter-threshold = '256'\n\
104          cleanup-interval = ' 1h'\n\
105          optimizing-compression-task-timeout = '30  m'\n\
106          allowed-clock-drift-for-files-from-future = '1\td'\n\
107          file-count-soft-limit = '\t \t65536\t'\n\
108          files-total-size-soft-limit = '512\t\t Mi '\n\
109          file-count-limit-percent-if-deleting = '70\t%'\n\
110          files-total-size-limit-percent-if-deleting = ' 70 %'",
111         cd
112     );
113     check_conf(&conf, &cd);
114 
115     fn check_conf(conf: &CacheConfig, cd: &PathBuf) {
116         assert!(conf.enabled());
117         assert_eq!(
118             conf.directory(),
119             &fs::canonicalize(cd).expect("canonicalize failed")
120         );
121         assert_eq!(conf.worker_event_queue_size(), 0x10);
122         assert_eq!(conf.baseline_compression_level(), 3);
123         assert_eq!(conf.optimized_compression_level(), 20);
124         assert_eq!(conf.optimized_compression_usage_counter_threshold(), 0x100);
125         assert_eq!(conf.cleanup_interval(), Duration::from_secs(60 * 60));
126         assert_eq!(
127             conf.optimizing_compression_task_timeout(),
128             Duration::from_secs(30 * 60)
129         );
130         assert_eq!(
131             conf.allowed_clock_drift_for_files_from_future(),
132             Duration::from_secs(60 * 60 * 24)
133         );
134         assert_eq!(conf.file_count_soft_limit(), 0x10_000);
135         assert_eq!(conf.files_total_size_soft_limit(), 512 * (1u64 << 20));
136         assert_eq!(conf.file_count_limit_percent_if_deleting(), 70);
137         assert_eq!(conf.files_total_size_limit_percent_if_deleting(), 70);
138     }
139 }
140 
141 #[test]
142 fn test_compression_level_settings() {
143     let (_td, cd, cp) = test_prolog();
144     let conf = load_config!(
145         cp,
146         "[cache]\n\
147          enabled = true\n\
148          directory = '{cache_dir}'\n\
149          baseline-compression-level = 1\n\
150          optimized-compression-level = 21",
151         cd
152     );
153     assert!(conf.enabled());
154     assert_eq!(conf.baseline_compression_level(), 1);
155     assert_eq!(conf.optimized_compression_level(), 21);
156 
157     bad_config!(
158         cp,
159         "[cache]\n\
160          enabled = true\n\
161          directory = '{cache_dir}'\n\
162          baseline-compression-level = -1\n\
163          optimized-compression-level = 21",
164         cd
165     );
166 
167     bad_config!(
168         cp,
169         "[cache]\n\
170          enabled = true\n\
171          directory = '{cache_dir}'\n\
172          baseline-compression-level = 15\n\
173          optimized-compression-level = 10",
174         cd
175     );
176 }
177 
178 #[test]
179 fn test_si_prefix_settings() {
180     let (_td, cd, cp) = test_prolog();
181     let conf = load_config!(
182         cp,
183         "[cache]\n\
184          enabled = true\n\
185          directory = '{cache_dir}'\n\
186          worker-event-queue-size = '42'\n\
187          optimized-compression-usage-counter-threshold = '4K'\n\
188          file-count-soft-limit = '3M'",
189         cd
190     );
191     assert!(conf.enabled());
192     assert_eq!(conf.worker_event_queue_size(), 42);
193     assert_eq!(conf.optimized_compression_usage_counter_threshold(), 4_000);
194     assert_eq!(conf.file_count_soft_limit(), 3_000_000);
195 
196     let conf = load_config!(
197         cp,
198         "[cache]\n\
199          enabled = true\n\
200          directory = '{cache_dir}'\n\
201          worker-event-queue-size = '2K'\n\
202          optimized-compression-usage-counter-threshold = '4444T'\n\
203          file-count-soft-limit = '1P'",
204         cd
205     );
206     assert!(conf.enabled());
207     assert_eq!(conf.worker_event_queue_size(), 2_000);
208     assert_eq!(
209         conf.optimized_compression_usage_counter_threshold(),
210         4_444_000_000_000_000
211     );
212     assert_eq!(conf.file_count_soft_limit(), 1_000_000_000_000_000);
213 
214     // different errors
215     bad_config!(
216         cp,
217         "[cache]\n\
218          enabled = true\n\
219          directory = '{cache_dir}'\n\
220          worker-event-queue-size = '2g'",
221         cd
222     );
223 
224     bad_config!(
225         cp,
226         "[cache]\n\
227          enabled = true\n\
228          directory = '{cache_dir}'\n\
229          file-count-soft-limit = 1",
230         cd
231     );
232 
233     bad_config!(
234         cp,
235         "[cache]\n\
236          enabled = true\n\
237          directory = '{cache_dir}'\n\
238          file-count-soft-limit = '-31337'",
239         cd
240     );
241 
242     bad_config!(
243         cp,
244         "[cache]\n\
245          enabled = true\n\
246          directory = '{cache_dir}'\n\
247          file-count-soft-limit = '3.14M'",
248         cd
249     );
250 }
251 
252 #[test]
253 fn test_disk_space_settings() {
254     let (_td, cd, cp) = test_prolog();
255     let conf = load_config!(
256         cp,
257         "[cache]\n\
258          enabled = true\n\
259          directory = '{cache_dir}'\n\
260          files-total-size-soft-limit = '76'",
261         cd
262     );
263     assert!(conf.enabled());
264     assert_eq!(conf.files_total_size_soft_limit(), 76);
265 
266     let conf = load_config!(
267         cp,
268         "[cache]\n\
269          enabled = true\n\
270          directory = '{cache_dir}'\n\
271          files-total-size-soft-limit = '42 Mi'",
272         cd
273     );
274     assert!(conf.enabled());
275     assert_eq!(conf.files_total_size_soft_limit(), 42 * (1u64 << 20));
276 
277     let conf = load_config!(
278         cp,
279         "[cache]\n\
280          enabled = true\n\
281          directory = '{cache_dir}'\n\
282          files-total-size-soft-limit = '2 Gi'",
283         cd
284     );
285     assert!(conf.enabled());
286     assert_eq!(conf.files_total_size_soft_limit(), 2 * (1u64 << 30));
287 
288     let conf = load_config!(
289         cp,
290         "[cache]\n\
291          enabled = true\n\
292          directory = '{cache_dir}'\n\
293          files-total-size-soft-limit = '31337 Ti'",
294         cd
295     );
296     assert!(conf.enabled());
297     assert_eq!(conf.files_total_size_soft_limit(), 31337 * (1u64 << 40));
298 
299     let conf = load_config!(
300         cp,
301         "[cache]\n\
302          enabled = true\n\
303          directory = '{cache_dir}'\n\
304          files-total-size-soft-limit = '7 Pi'",
305         cd
306     );
307     assert!(conf.enabled());
308     assert_eq!(conf.files_total_size_soft_limit(), 7 * (1u64 << 50));
309 
310     let conf = load_config!(
311         cp,
312         "[cache]\n\
313          enabled = true\n\
314          directory = '{cache_dir}'\n\
315          files-total-size-soft-limit = '7M'",
316         cd
317     );
318     assert!(conf.enabled());
319     assert_eq!(conf.files_total_size_soft_limit(), 7_000_000);
320 
321     // different errors
322     bad_config!(
323         cp,
324         "[cache]\n\
325          enabled = true\n\
326          directory = '{cache_dir}'\n\
327          files-total-size-soft-limit = '7 mi'",
328         cd
329     );
330 
331     bad_config!(
332         cp,
333         "[cache]\n\
334          enabled = true\n\
335          directory = '{cache_dir}'\n\
336          files-total-size-soft-limit = 1",
337         cd
338     );
339 
340     bad_config!(
341         cp,
342         "[cache]\n\
343          enabled = true\n\
344          directory = '{cache_dir}'\n\
345          files-total-size-soft-limit = '-31337'",
346         cd
347     );
348 
349     bad_config!(
350         cp,
351         "[cache]\n\
352          enabled = true\n\
353          directory = '{cache_dir}'\n\
354          files-total-size-soft-limit = '3.14Ki'",
355         cd
356     );
357 }
358 
359 #[test]
360 fn test_duration_settings() {
361     let (_td, cd, cp) = test_prolog();
362     let conf = load_config!(
363         cp,
364         "[cache]\n\
365          enabled = true\n\
366          directory = '{cache_dir}'\n\
367          cleanup-interval = '100s'\n\
368          optimizing-compression-task-timeout = '3m'\n\
369          allowed-clock-drift-for-files-from-future = '4h'",
370         cd
371     );
372     assert!(conf.enabled());
373     assert_eq!(conf.cleanup_interval(), Duration::from_secs(100));
374     assert_eq!(
375         conf.optimizing_compression_task_timeout(),
376         Duration::from_secs(3 * 60)
377     );
378     assert_eq!(
379         conf.allowed_clock_drift_for_files_from_future(),
380         Duration::from_secs(4 * 60 * 60)
381     );
382 
383     let conf = load_config!(
384         cp,
385         "[cache]\n\
386          enabled = true\n\
387          directory = '{cache_dir}'\n\
388          cleanup-interval = '2d'\n\
389          optimizing-compression-task-timeout = '333 m'",
390         cd
391     );
392     assert!(conf.enabled());
393     assert_eq!(
394         conf.cleanup_interval(),
395         Duration::from_secs(2 * 24 * 60 * 60)
396     );
397     assert_eq!(
398         conf.optimizing_compression_task_timeout(),
399         Duration::from_secs(333 * 60)
400     );
401 
402     // different errors
403     bad_config!(
404         cp,
405         "[cache]\n\
406          enabled = true\n\
407          directory = '{cache_dir}'\n\
408          optimizing-compression-task-timeout = '333'",
409         cd
410     );
411 
412     bad_config!(
413         cp,
414         "[cache]\n\
415          enabled = true\n\
416          directory = '{cache_dir}'\n\
417          optimizing-compression-task-timeout = 333",
418         cd
419     );
420 
421     bad_config!(
422         cp,
423         "[cache]\n\
424          enabled = true\n\
425          directory = '{cache_dir}'\n\
426          optimizing-compression-task-timeout = '10 M'",
427         cd
428     );
429 
430     bad_config!(
431         cp,
432         "[cache]\n\
433          enabled = true\n\
434          directory = '{cache_dir}'\n\
435          optimizing-compression-task-timeout = '10 min'",
436         cd
437     );
438 
439     bad_config!(
440         cp,
441         "[cache]\n\
442          enabled = true\n\
443          directory = '{cache_dir}'\n\
444          optimizing-compression-task-timeout = '-10s'",
445         cd
446     );
447 
448     bad_config!(
449         cp,
450         "[cache]\n\
451          enabled = true\n\
452          directory = '{cache_dir}'\n\
453          optimizing-compression-task-timeout = '1.5m'",
454         cd
455     );
456 }
457 
458 #[test]
459 fn test_percent_settings() {
460     let (_td, cd, cp) = test_prolog();
461     let conf = load_config!(
462         cp,
463         "[cache]\n\
464          enabled = true\n\
465          directory = '{cache_dir}'\n\
466          file-count-limit-percent-if-deleting = '62%'\n\
467          files-total-size-limit-percent-if-deleting = '23 %'",
468         cd
469     );
470     assert!(conf.enabled());
471     assert_eq!(conf.file_count_limit_percent_if_deleting(), 62);
472     assert_eq!(conf.files_total_size_limit_percent_if_deleting(), 23);
473 
474     // different errors
475     bad_config!(
476         cp,
477         "[cache]\n\
478          enabled = true\n\
479          directory = '{cache_dir}'\n\
480          files-total-size-limit-percent-if-deleting = '23'",
481         cd
482     );
483 
484     bad_config!(
485         cp,
486         "[cache]\n\
487          enabled = true\n\
488          directory = '{cache_dir}'\n\
489          files-total-size-limit-percent-if-deleting = '22.5%'",
490         cd
491     );
492 
493     bad_config!(
494         cp,
495         "[cache]\n\
496          enabled = true\n\
497          directory = '{cache_dir}'\n\
498          files-total-size-limit-percent-if-deleting = '0.5'",
499         cd
500     );
501 
502     bad_config!(
503         cp,
504         "[cache]\n\
505          enabled = true\n\
506          directory = '{cache_dir}'\n\
507          files-total-size-limit-percent-if-deleting = '-1%'",
508         cd
509     );
510 
511     bad_config!(
512         cp,
513         "[cache]\n\
514          enabled = true\n\
515          directory = '{cache_dir}'\n\
516          files-total-size-limit-percent-if-deleting = '101%'",
517         cd
518     );
519 }
520