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 _ = 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] 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] 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] 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] 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] 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] 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] 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] 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] 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