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