108f9eb17SAlex Crichton use super::*;
208f9eb17SAlex Crichton use crate::config::tests::test_prolog;
308f9eb17SAlex Crichton use std::iter::repeat;
408f9eb17SAlex Crichton use std::process;
508f9eb17SAlex Crichton // load_config! comes from crate::cache(::config::tests);
608f9eb17SAlex Crichton 
708f9eb17SAlex Crichton // when doing anything with the tests, make sure they are DETERMINISTIC
808f9eb17SAlex Crichton // -- the result shouldn't rely on system time!
908f9eb17SAlex Crichton pub mod system_time_stub;
1008f9eb17SAlex Crichton 
1108f9eb17SAlex Crichton #[test]
test_on_get_create_stats_file()1208f9eb17SAlex Crichton fn test_on_get_create_stats_file() {
1308f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
1408f9eb17SAlex Crichton     let cache_config = load_config!(
1508f9eb17SAlex Crichton         config_path,
1608f9eb17SAlex Crichton         "[cache]\n\
1708c7359fSAlex Crichton          directory = '{cache_dir}'",
1808f9eb17SAlex Crichton         cache_dir
1908f9eb17SAlex Crichton     );
201d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
2108f9eb17SAlex Crichton 
2208f9eb17SAlex Crichton     let mod_file = cache_dir.join("some-mod");
2308f9eb17SAlex Crichton     worker.on_cache_get_async(mod_file);
2408f9eb17SAlex Crichton     worker.wait_for_all_events_handled();
2508f9eb17SAlex Crichton     assert_eq!(worker.events_dropped(), 0);
2608f9eb17SAlex Crichton 
2708f9eb17SAlex Crichton     let stats_file = cache_dir.join("some-mod.stats");
2808f9eb17SAlex Crichton     let stats = read_stats_file(&stats_file).expect("Failed to read stats file");
2908f9eb17SAlex Crichton     assert_eq!(stats.usages, 1);
3008f9eb17SAlex Crichton     assert_eq!(
3108f9eb17SAlex Crichton         stats.compression_level,
3208f9eb17SAlex Crichton         cache_config.baseline_compression_level()
3308f9eb17SAlex Crichton     );
3408f9eb17SAlex Crichton }
3508f9eb17SAlex Crichton 
3608f9eb17SAlex Crichton #[test]
test_on_get_update_usage_counter()3708f9eb17SAlex Crichton fn test_on_get_update_usage_counter() {
3808f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
3908f9eb17SAlex Crichton     let cache_config = load_config!(
4008f9eb17SAlex Crichton         config_path,
4108f9eb17SAlex Crichton         "[cache]\n\
4208c7359fSAlex Crichton          directory = '{cache_dir}'\n\
4308f9eb17SAlex Crichton          worker-event-queue-size = '16'",
4408f9eb17SAlex Crichton         cache_dir
4508f9eb17SAlex Crichton     );
461d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
4708f9eb17SAlex Crichton 
4808f9eb17SAlex Crichton     let mod_file = cache_dir.join("some-mod");
4908f9eb17SAlex Crichton     let stats_file = cache_dir.join("some-mod.stats");
5008f9eb17SAlex Crichton     let default_stats = ModuleCacheStatistics::default(&cache_config);
5108f9eb17SAlex Crichton     assert!(write_stats_file(&stats_file, &default_stats));
5208f9eb17SAlex Crichton 
5308f9eb17SAlex Crichton     let mut usages = 0;
5408f9eb17SAlex Crichton     for times_used in &[4, 7, 2] {
5508f9eb17SAlex Crichton         for _ in 0..*times_used {
5608f9eb17SAlex Crichton             worker.on_cache_get_async(mod_file.clone());
5708f9eb17SAlex Crichton             usages += 1;
5808f9eb17SAlex Crichton         }
5908f9eb17SAlex Crichton 
6008f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
6108f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
6208f9eb17SAlex Crichton 
6308f9eb17SAlex Crichton         let stats = read_stats_file(&stats_file).expect("Failed to read stats file");
6408f9eb17SAlex Crichton         assert_eq!(stats.usages, usages);
6508f9eb17SAlex Crichton     }
6608f9eb17SAlex Crichton }
6708f9eb17SAlex Crichton 
6808f9eb17SAlex Crichton #[test]
test_on_get_recompress_no_mod_file()6908f9eb17SAlex Crichton fn test_on_get_recompress_no_mod_file() {
7008f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
7108f9eb17SAlex Crichton     let cache_config = load_config!(
7208f9eb17SAlex Crichton         config_path,
7308f9eb17SAlex Crichton         "[cache]\n\
7408c7359fSAlex Crichton          directory = '{cache_dir}'\n\
7508f9eb17SAlex Crichton          worker-event-queue-size = '16'\n\
7608f9eb17SAlex Crichton          baseline-compression-level = 3\n\
7708f9eb17SAlex Crichton          optimized-compression-level = 7\n\
7808f9eb17SAlex Crichton          optimized-compression-usage-counter-threshold = '256'",
7908f9eb17SAlex Crichton         cache_dir
8008f9eb17SAlex Crichton     );
811d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
8208f9eb17SAlex Crichton 
8308f9eb17SAlex Crichton     let mod_file = cache_dir.join("some-mod");
8408f9eb17SAlex Crichton     let stats_file = cache_dir.join("some-mod.stats");
8508f9eb17SAlex Crichton     let mut start_stats = ModuleCacheStatistics::default(&cache_config);
8608f9eb17SAlex Crichton     start_stats.usages = 250;
8708f9eb17SAlex Crichton     assert!(write_stats_file(&stats_file, &start_stats));
8808f9eb17SAlex Crichton 
8908f9eb17SAlex Crichton     let mut usages = start_stats.usages;
9008f9eb17SAlex Crichton     for times_used in &[4, 7, 2] {
9108f9eb17SAlex Crichton         for _ in 0..*times_used {
9208f9eb17SAlex Crichton             worker.on_cache_get_async(mod_file.clone());
9308f9eb17SAlex Crichton             usages += 1;
9408f9eb17SAlex Crichton         }
9508f9eb17SAlex Crichton 
9608f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
9708f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
9808f9eb17SAlex Crichton 
9908f9eb17SAlex Crichton         let stats = read_stats_file(&stats_file).expect("Failed to read stats file");
10008f9eb17SAlex Crichton         assert_eq!(stats.usages, usages);
10108f9eb17SAlex Crichton         assert_eq!(
10208f9eb17SAlex Crichton             stats.compression_level,
10308f9eb17SAlex Crichton             cache_config.baseline_compression_level()
10408f9eb17SAlex Crichton         );
10508f9eb17SAlex Crichton     }
10608f9eb17SAlex Crichton }
10708f9eb17SAlex Crichton 
10808f9eb17SAlex Crichton #[test]
test_on_get_recompress_with_mod_file()10908f9eb17SAlex Crichton fn test_on_get_recompress_with_mod_file() {
11008f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
11108f9eb17SAlex Crichton     let cache_config = load_config!(
11208f9eb17SAlex Crichton         config_path,
11308f9eb17SAlex Crichton         "[cache]\n\
11408c7359fSAlex Crichton          directory = '{cache_dir}'\n\
11508f9eb17SAlex Crichton          worker-event-queue-size = '16'\n\
11608f9eb17SAlex Crichton          baseline-compression-level = 3\n\
11708f9eb17SAlex Crichton          optimized-compression-level = 7\n\
11808f9eb17SAlex Crichton          optimized-compression-usage-counter-threshold = '256'",
11908f9eb17SAlex Crichton         cache_dir
12008f9eb17SAlex Crichton     );
1211d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
12208f9eb17SAlex Crichton 
12308f9eb17SAlex Crichton     let mod_file = cache_dir.join("some-mod");
12408f9eb17SAlex Crichton     let mod_data = "some test data to be compressed";
12508f9eb17SAlex Crichton     let data = zstd::encode_all(
12608f9eb17SAlex Crichton         mod_data.as_bytes(),
12708f9eb17SAlex Crichton         cache_config.baseline_compression_level(),
12808f9eb17SAlex Crichton     )
12908f9eb17SAlex Crichton     .expect("Failed to compress sample mod file");
13008f9eb17SAlex Crichton     fs::write(&mod_file, &data).expect("Failed to write sample mod file");
13108f9eb17SAlex Crichton 
13208f9eb17SAlex Crichton     let stats_file = cache_dir.join("some-mod.stats");
13308f9eb17SAlex Crichton     let mut start_stats = ModuleCacheStatistics::default(&cache_config);
13408f9eb17SAlex Crichton     start_stats.usages = 250;
13508f9eb17SAlex Crichton     assert!(write_stats_file(&stats_file, &start_stats));
13608f9eb17SAlex Crichton 
13708f9eb17SAlex Crichton     // scenarios:
13808f9eb17SAlex Crichton     // 1. Shouldn't be recompressed
13908f9eb17SAlex Crichton     // 2. Should be recompressed
14008f9eb17SAlex Crichton     // 3. After lowering compression level, should be recompressed
14108f9eb17SAlex Crichton     let scenarios = [(4, false), (7, true), (2, false)];
14208f9eb17SAlex Crichton 
14308f9eb17SAlex Crichton     let mut usages = start_stats.usages;
1441321c234SAlex Crichton     assert!(usages < cache_config.optimized_compression_usage_counter_threshold());
14508f9eb17SAlex Crichton     let mut tested_higher_opt_compr_lvl = false;
14608f9eb17SAlex Crichton     for (times_used, lower_compr_lvl) in &scenarios {
14708f9eb17SAlex Crichton         for _ in 0..*times_used {
14808f9eb17SAlex Crichton             worker.on_cache_get_async(mod_file.clone());
14908f9eb17SAlex Crichton             usages += 1;
15008f9eb17SAlex Crichton         }
15108f9eb17SAlex Crichton 
15208f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
15308f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
15408f9eb17SAlex Crichton 
15508f9eb17SAlex Crichton         let mut stats = read_stats_file(&stats_file).expect("Failed to read stats file");
15608f9eb17SAlex Crichton         assert_eq!(stats.usages, usages);
15708f9eb17SAlex Crichton         assert_eq!(
15808f9eb17SAlex Crichton             stats.compression_level,
15908f9eb17SAlex Crichton             if usages < cache_config.optimized_compression_usage_counter_threshold() {
16008f9eb17SAlex Crichton                 cache_config.baseline_compression_level()
16108f9eb17SAlex Crichton             } else {
16208f9eb17SAlex Crichton                 cache_config.optimized_compression_level()
16308f9eb17SAlex Crichton             }
16408f9eb17SAlex Crichton         );
16508f9eb17SAlex Crichton         let compressed_data = fs::read(&mod_file).expect("Failed to read mod file");
16608f9eb17SAlex Crichton         let decoded_data =
16708f9eb17SAlex Crichton             zstd::decode_all(&compressed_data[..]).expect("Failed to decompress mod file");
16808f9eb17SAlex Crichton         assert_eq!(decoded_data, mod_data.as_bytes());
16908f9eb17SAlex Crichton 
17008f9eb17SAlex Crichton         if *lower_compr_lvl {
1711321c234SAlex Crichton             assert!(usages >= cache_config.optimized_compression_usage_counter_threshold());
17208f9eb17SAlex Crichton             tested_higher_opt_compr_lvl = true;
17308f9eb17SAlex Crichton             stats.compression_level -= 1;
17408f9eb17SAlex Crichton             assert!(write_stats_file(&stats_file, &stats));
17508f9eb17SAlex Crichton         }
17608f9eb17SAlex Crichton     }
1771321c234SAlex Crichton     assert!(usages >= cache_config.optimized_compression_usage_counter_threshold());
17808f9eb17SAlex Crichton     assert!(tested_higher_opt_compr_lvl);
17908f9eb17SAlex Crichton }
18008f9eb17SAlex Crichton 
18108f9eb17SAlex Crichton #[test]
test_on_get_recompress_lock()18208f9eb17SAlex Crichton fn test_on_get_recompress_lock() {
18308f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
18408f9eb17SAlex Crichton     let cache_config = load_config!(
18508f9eb17SAlex Crichton         config_path,
18608f9eb17SAlex Crichton         "[cache]\n\
18708c7359fSAlex Crichton          directory = '{cache_dir}'\n\
18808f9eb17SAlex Crichton          worker-event-queue-size = '16'\n\
18908f9eb17SAlex Crichton          baseline-compression-level = 3\n\
19008f9eb17SAlex Crichton          optimized-compression-level = 7\n\
19108f9eb17SAlex Crichton          optimized-compression-usage-counter-threshold = '256'\n\
19208f9eb17SAlex Crichton          optimizing-compression-task-timeout = '30m'\n\
19308f9eb17SAlex Crichton          allowed-clock-drift-for-files-from-future = '1d'",
19408f9eb17SAlex Crichton         cache_dir
19508f9eb17SAlex Crichton     );
1961d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
19708f9eb17SAlex Crichton 
19808f9eb17SAlex Crichton     let mod_file = cache_dir.join("some-mod");
19908f9eb17SAlex Crichton     let mod_data = "some test data to be compressed";
20008f9eb17SAlex Crichton     let data = zstd::encode_all(
20108f9eb17SAlex Crichton         mod_data.as_bytes(),
20208f9eb17SAlex Crichton         cache_config.baseline_compression_level(),
20308f9eb17SAlex Crichton     )
20408f9eb17SAlex Crichton     .expect("Failed to compress sample mod file");
20508f9eb17SAlex Crichton     fs::write(&mod_file, &data).expect("Failed to write sample mod file");
20608f9eb17SAlex Crichton 
20708f9eb17SAlex Crichton     let stats_file = cache_dir.join("some-mod.stats");
20808f9eb17SAlex Crichton     let mut start_stats = ModuleCacheStatistics::default(&cache_config);
20908f9eb17SAlex Crichton     start_stats.usages = 255;
21008f9eb17SAlex Crichton 
21108f9eb17SAlex Crichton     let lock_file = cache_dir.join("some-mod.wip-lock");
21208f9eb17SAlex Crichton 
21308f9eb17SAlex Crichton     let scenarios = [
21408f9eb17SAlex Crichton         // valid lock
21508f9eb17SAlex Crichton         (true, "past", Duration::from_secs(30 * 60 - 1)),
21608f9eb17SAlex Crichton         // valid future lock
21708f9eb17SAlex Crichton         (true, "future", Duration::from_secs(24 * 60 * 60)),
21808f9eb17SAlex Crichton         // expired lock
21908f9eb17SAlex Crichton         (false, "past", Duration::from_secs(30 * 60)),
22008f9eb17SAlex Crichton         // expired future lock
22108f9eb17SAlex Crichton         (false, "future", Duration::from_secs(24 * 60 * 60 + 1)),
22208f9eb17SAlex Crichton     ];
22308f9eb17SAlex Crichton 
22408f9eb17SAlex Crichton     for (lock_valid, duration_sign, duration) in &scenarios {
22508f9eb17SAlex Crichton         assert!(write_stats_file(&stats_file, &start_stats)); // restore usage & compression level
22608f9eb17SAlex Crichton         create_file_with_mtime(&lock_file, "", duration_sign, &duration);
22708f9eb17SAlex Crichton 
22808f9eb17SAlex Crichton         worker.on_cache_get_async(mod_file.clone());
22908f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
23008f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
23108f9eb17SAlex Crichton 
23208f9eb17SAlex Crichton         let stats = read_stats_file(&stats_file).expect("Failed to read stats file");
23308f9eb17SAlex Crichton         assert_eq!(stats.usages, start_stats.usages + 1);
23408f9eb17SAlex Crichton         assert_eq!(
23508f9eb17SAlex Crichton             stats.compression_level,
23608f9eb17SAlex Crichton             if *lock_valid {
23708f9eb17SAlex Crichton                 cache_config.baseline_compression_level()
23808f9eb17SAlex Crichton             } else {
23908f9eb17SAlex Crichton                 cache_config.optimized_compression_level()
24008f9eb17SAlex Crichton             }
24108f9eb17SAlex Crichton         );
24208f9eb17SAlex Crichton         let compressed_data = fs::read(&mod_file).expect("Failed to read mod file");
24308f9eb17SAlex Crichton         let decoded_data =
24408f9eb17SAlex Crichton             zstd::decode_all(&compressed_data[..]).expect("Failed to decompress mod file");
24508f9eb17SAlex Crichton         assert_eq!(decoded_data, mod_data.as_bytes());
24608f9eb17SAlex Crichton     }
24708f9eb17SAlex Crichton }
24808f9eb17SAlex Crichton 
24908f9eb17SAlex Crichton #[test]
test_on_update_fresh_stats_file()25008f9eb17SAlex Crichton fn test_on_update_fresh_stats_file() {
25108f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
25208f9eb17SAlex Crichton     let cache_config = load_config!(
25308f9eb17SAlex Crichton         config_path,
25408f9eb17SAlex Crichton         "[cache]\n\
25508c7359fSAlex Crichton          directory = '{cache_dir}'\n\
25608f9eb17SAlex Crichton          worker-event-queue-size = '16'\n\
25708f9eb17SAlex Crichton          baseline-compression-level = 3\n\
25808f9eb17SAlex Crichton          optimized-compression-level = 7\n\
25908f9eb17SAlex Crichton          cleanup-interval = '1h'",
26008f9eb17SAlex Crichton         cache_dir
26108f9eb17SAlex Crichton     );
2621d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
26308f9eb17SAlex Crichton 
26408f9eb17SAlex Crichton     let mod_file = cache_dir.join("some-mod");
26508f9eb17SAlex Crichton     let stats_file = cache_dir.join("some-mod.stats");
26608f9eb17SAlex Crichton     let cleanup_certificate = cache_dir.join(".cleanup.wip-done");
26708f9eb17SAlex Crichton     create_file_with_mtime(&cleanup_certificate, "", "future", &Duration::from_secs(0));
26808f9eb17SAlex Crichton     // the below created by the worker if it cleans up
26908f9eb17SAlex Crichton     let worker_lock_file = cache_dir.join(format!(".cleanup.wip-{}", process::id()));
27008f9eb17SAlex Crichton 
27108f9eb17SAlex Crichton     // scenarios:
27208f9eb17SAlex Crichton     // 1. Create new stats file
27308f9eb17SAlex Crichton     // 2. Overwrite existing file
27408f9eb17SAlex Crichton     for update_file in &[true, false] {
27508f9eb17SAlex Crichton         worker.on_cache_update_async(mod_file.clone());
27608f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
27708f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
27808f9eb17SAlex Crichton 
27908f9eb17SAlex Crichton         let mut stats = read_stats_file(&stats_file).expect("Failed to read stats file");
28008f9eb17SAlex Crichton         assert_eq!(stats.usages, 1);
28108f9eb17SAlex Crichton         assert_eq!(
28208f9eb17SAlex Crichton             stats.compression_level,
28308f9eb17SAlex Crichton             cache_config.baseline_compression_level()
28408f9eb17SAlex Crichton         );
28508f9eb17SAlex Crichton 
28608f9eb17SAlex Crichton         if *update_file {
28708f9eb17SAlex Crichton             stats.usages += 42;
28808f9eb17SAlex Crichton             stats.compression_level += 1;
28908f9eb17SAlex Crichton             assert!(write_stats_file(&stats_file, &stats));
29008f9eb17SAlex Crichton         }
29108f9eb17SAlex Crichton 
29208f9eb17SAlex Crichton         assert!(!worker_lock_file.exists());
29308f9eb17SAlex Crichton     }
29408f9eb17SAlex Crichton }
29508f9eb17SAlex Crichton 
29608f9eb17SAlex Crichton #[test]
test_on_update_cleanup_limits_trash_locks()29708f9eb17SAlex Crichton fn test_on_update_cleanup_limits_trash_locks() {
29808f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
29908f9eb17SAlex Crichton     let cache_config = load_config!(
30008f9eb17SAlex Crichton         config_path,
30108f9eb17SAlex Crichton         "[cache]\n\
30208c7359fSAlex Crichton          directory = '{cache_dir}'\n\
30308f9eb17SAlex Crichton          worker-event-queue-size = '16'\n\
30408f9eb17SAlex Crichton          cleanup-interval = '30m'\n\
30508f9eb17SAlex Crichton          optimizing-compression-task-timeout = '30m'\n\
30608f9eb17SAlex Crichton          allowed-clock-drift-for-files-from-future = '1d'\n\
30708f9eb17SAlex Crichton          file-count-soft-limit = '5'\n\
30808f9eb17SAlex Crichton          files-total-size-soft-limit = '30K'\n\
30908f9eb17SAlex Crichton          file-count-limit-percent-if-deleting = '70%'\n\
31008f9eb17SAlex Crichton          files-total-size-limit-percent-if-deleting = '70%'
31108f9eb17SAlex Crichton          ",
31208f9eb17SAlex Crichton         cache_dir
31308f9eb17SAlex Crichton     );
3141d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
31508f9eb17SAlex Crichton     let content_1k = "a".repeat(1_000);
31608f9eb17SAlex Crichton     let content_10k = "a".repeat(10_000);
31708f9eb17SAlex Crichton 
31808f9eb17SAlex Crichton     let mods_files_dir = cache_dir.join("target-triple").join("compiler-version");
31908f9eb17SAlex Crichton     let mod_with_stats = mods_files_dir.join("mod-with-stats");
32008f9eb17SAlex Crichton     let trash_dirs = [
32108f9eb17SAlex Crichton         mods_files_dir.join("trash"),
32208f9eb17SAlex Crichton         mods_files_dir.join("trash").join("trash"),
32308f9eb17SAlex Crichton     ];
32408f9eb17SAlex Crichton     let trash_files = [
32508f9eb17SAlex Crichton         cache_dir.join("trash-file"),
32608f9eb17SAlex Crichton         cache_dir.join("trash-file.wip-lock"),
32708f9eb17SAlex Crichton         cache_dir.join("target-triple").join("trash.txt"),
32808f9eb17SAlex Crichton         cache_dir.join("target-triple").join("trash.txt.wip-lock"),
32908f9eb17SAlex Crichton         mods_files_dir.join("trash.ogg"),
33008f9eb17SAlex Crichton         mods_files_dir.join("trash").join("trash.doc"),
33108f9eb17SAlex Crichton         mods_files_dir.join("trash").join("trash.doc.wip-lock"),
33208f9eb17SAlex Crichton         mods_files_dir.join("trash").join("trash").join("trash.xls"),
33308f9eb17SAlex Crichton         mods_files_dir
33408f9eb17SAlex Crichton             .join("trash")
33508f9eb17SAlex Crichton             .join("trash")
33608f9eb17SAlex Crichton             .join("trash.xls.wip-lock"),
33708f9eb17SAlex Crichton     ];
33808f9eb17SAlex Crichton     let mod_locks = [
33908f9eb17SAlex Crichton         // valid lock
34008f9eb17SAlex Crichton         (
34108f9eb17SAlex Crichton             mods_files_dir.join("mod0.wip-lock"),
34208f9eb17SAlex Crichton             true,
34308f9eb17SAlex Crichton             "past",
34408f9eb17SAlex Crichton             Duration::from_secs(30 * 60 - 1),
34508f9eb17SAlex Crichton         ),
34608f9eb17SAlex Crichton         // valid future lock
34708f9eb17SAlex Crichton         (
34808f9eb17SAlex Crichton             mods_files_dir.join("mod1.wip-lock"),
34908f9eb17SAlex Crichton             true,
35008f9eb17SAlex Crichton             "future",
35108f9eb17SAlex Crichton             Duration::from_secs(24 * 60 * 60),
35208f9eb17SAlex Crichton         ),
35308f9eb17SAlex Crichton         // expired lock
35408f9eb17SAlex Crichton         (
35508f9eb17SAlex Crichton             mods_files_dir.join("mod2.wip-lock"),
35608f9eb17SAlex Crichton             false,
35708f9eb17SAlex Crichton             "past",
35808f9eb17SAlex Crichton             Duration::from_secs(30 * 60),
35908f9eb17SAlex Crichton         ),
36008f9eb17SAlex Crichton         // expired future lock
36108f9eb17SAlex Crichton         (
36208f9eb17SAlex Crichton             mods_files_dir.join("mod3.wip-lock"),
36308f9eb17SAlex Crichton             false,
36408f9eb17SAlex Crichton             "future",
36508f9eb17SAlex Crichton             Duration::from_secs(24 * 60 * 60 + 1),
36608f9eb17SAlex Crichton         ),
36708f9eb17SAlex Crichton     ];
36808f9eb17SAlex Crichton     // the below created by the worker if it cleans up
36908f9eb17SAlex Crichton     let worker_lock_file = cache_dir.join(format!(".cleanup.wip-{}", process::id()));
37008f9eb17SAlex Crichton 
37108f9eb17SAlex Crichton     let scenarios = [
37208f9eb17SAlex Crichton         // Close to limits, but not reached, only trash deleted
37308f9eb17SAlex Crichton         (2, 2, 4),
37408f9eb17SAlex Crichton         // File count limit exceeded
37508f9eb17SAlex Crichton         (1, 10, 3),
37608f9eb17SAlex Crichton         // Total size limit exceeded
37708f9eb17SAlex Crichton         (4, 0, 2),
37808f9eb17SAlex Crichton         // Both limits exceeded
37908f9eb17SAlex Crichton         (3, 5, 3),
38008f9eb17SAlex Crichton     ];
38108f9eb17SAlex Crichton 
38208f9eb17SAlex Crichton     for (files_10k, files_1k, remaining_files) in &scenarios {
38308f9eb17SAlex Crichton         let mut secs_ago = 100;
38408f9eb17SAlex Crichton 
38508f9eb17SAlex Crichton         for d in &trash_dirs {
38608f9eb17SAlex Crichton             fs::create_dir_all(d).expect("Failed to create directories");
38708f9eb17SAlex Crichton         }
38808f9eb17SAlex Crichton         for f in &trash_files {
38908f9eb17SAlex Crichton             create_file_with_mtime(f, "", "past", &Duration::from_secs(0));
39008f9eb17SAlex Crichton         }
39108f9eb17SAlex Crichton         for (f, _, sign, duration) in &mod_locks {
39208f9eb17SAlex Crichton             create_file_with_mtime(f, "", sign, &duration);
39308f9eb17SAlex Crichton         }
39408f9eb17SAlex Crichton 
39508f9eb17SAlex Crichton         let mut mods_paths = vec![];
39608f9eb17SAlex Crichton         for content in repeat(&content_10k)
39708f9eb17SAlex Crichton             .take(*files_10k)
39808f9eb17SAlex Crichton             .chain(repeat(&content_1k).take(*files_1k))
39908f9eb17SAlex Crichton         {
40008f9eb17SAlex Crichton             mods_paths.push(mods_files_dir.join(format!("test-mod-{}", mods_paths.len())));
40108f9eb17SAlex Crichton             create_file_with_mtime(
40208f9eb17SAlex Crichton                 mods_paths.last().unwrap(),
40308f9eb17SAlex Crichton                 content,
40408f9eb17SAlex Crichton                 "past",
40508f9eb17SAlex Crichton                 &Duration::from_secs(secs_ago),
40608f9eb17SAlex Crichton             );
4071321c234SAlex Crichton             assert!(secs_ago > 0);
40808f9eb17SAlex Crichton             secs_ago -= 1;
40908f9eb17SAlex Crichton         }
41008f9eb17SAlex Crichton 
41108f9eb17SAlex Crichton         // creating .stats file updates mtime what affects test results
41208f9eb17SAlex Crichton         // so we use a separate nonexistent module here (orphaned .stats will be removed anyway)
41308f9eb17SAlex Crichton         worker.on_cache_update_async(mod_with_stats.clone());
41408f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
41508f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
41608f9eb17SAlex Crichton 
41708f9eb17SAlex Crichton         for ent in trash_dirs.iter().chain(trash_files.iter()) {
41808f9eb17SAlex Crichton             assert!(!ent.exists());
41908f9eb17SAlex Crichton         }
42008f9eb17SAlex Crichton         for (f, valid, ..) in &mod_locks {
42108f9eb17SAlex Crichton             assert_eq!(f.exists(), *valid);
42208f9eb17SAlex Crichton         }
42308f9eb17SAlex Crichton         for (idx, path) in mods_paths.iter().enumerate() {
42408f9eb17SAlex Crichton             let should_exist = idx >= mods_paths.len() - *remaining_files;
42508f9eb17SAlex Crichton             assert_eq!(path.exists(), should_exist);
42608f9eb17SAlex Crichton             if should_exist {
42708f9eb17SAlex Crichton                 // cleanup before next iteration
42808f9eb17SAlex Crichton                 fs::remove_file(path).expect("Failed to remove a file");
42908f9eb17SAlex Crichton             }
43008f9eb17SAlex Crichton         }
43108f9eb17SAlex Crichton         fs::remove_file(&worker_lock_file).expect("Failed to remove lock file");
43208f9eb17SAlex Crichton     }
43308f9eb17SAlex Crichton }
43408f9eb17SAlex Crichton 
43508f9eb17SAlex Crichton #[test]
test_on_update_cleanup_lru_policy()43608f9eb17SAlex Crichton fn test_on_update_cleanup_lru_policy() {
43708f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
43808f9eb17SAlex Crichton     let cache_config = load_config!(
43908f9eb17SAlex Crichton         config_path,
44008f9eb17SAlex Crichton         "[cache]\n\
44108c7359fSAlex Crichton          directory = '{cache_dir}'\n\
44208f9eb17SAlex Crichton          worker-event-queue-size = '16'\n\
44308f9eb17SAlex Crichton          file-count-soft-limit = '5'\n\
44408f9eb17SAlex Crichton          files-total-size-soft-limit = '30K'\n\
44508f9eb17SAlex Crichton          file-count-limit-percent-if-deleting = '80%'\n\
44608f9eb17SAlex Crichton          files-total-size-limit-percent-if-deleting = '70%'",
44708f9eb17SAlex Crichton         cache_dir
44808f9eb17SAlex Crichton     );
4491d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
45008f9eb17SAlex Crichton     let content_1k = "a".repeat(1_000);
45108f9eb17SAlex Crichton     let content_5k = "a".repeat(5_000);
45208f9eb17SAlex Crichton     let content_10k = "a".repeat(10_000);
45308f9eb17SAlex Crichton 
45408f9eb17SAlex Crichton     let mods_files_dir = cache_dir.join("target-triple").join("compiler-version");
45508f9eb17SAlex Crichton     fs::create_dir_all(&mods_files_dir).expect("Failed to create directories");
45608f9eb17SAlex Crichton     let nonexistent_mod_file = cache_dir.join("nonexistent-mod");
45708f9eb17SAlex Crichton     let orphaned_stats_file = cache_dir.join("orphaned-mod.stats");
45808f9eb17SAlex Crichton     let worker_lock_file = cache_dir.join(format!(".cleanup.wip-{}", process::id()));
45908f9eb17SAlex Crichton 
46008f9eb17SAlex Crichton     // content, how long ago created, how long ago stats created (if created), should be alive
46108f9eb17SAlex Crichton     let scenarios = [
46208f9eb17SAlex Crichton         &[
46308f9eb17SAlex Crichton             (&content_10k, 29, None, false),
46408f9eb17SAlex Crichton             (&content_10k, 28, None, false),
46508f9eb17SAlex Crichton             (&content_10k, 27, None, false),
46608f9eb17SAlex Crichton             (&content_1k, 26, None, true),
46708f9eb17SAlex Crichton             (&content_10k, 25, None, true),
46808f9eb17SAlex Crichton             (&content_1k, 24, None, true),
46908f9eb17SAlex Crichton         ],
47008f9eb17SAlex Crichton         &[
47108f9eb17SAlex Crichton             (&content_10k, 29, None, false),
47208f9eb17SAlex Crichton             (&content_10k, 28, None, false),
47308f9eb17SAlex Crichton             (&content_10k, 27, None, true),
47408f9eb17SAlex Crichton             (&content_1k, 26, None, true),
47508f9eb17SAlex Crichton             (&content_5k, 25, None, true),
47608f9eb17SAlex Crichton             (&content_1k, 24, None, true),
47708f9eb17SAlex Crichton         ],
47808f9eb17SAlex Crichton         &[
47908f9eb17SAlex Crichton             (&content_10k, 29, Some(19), true),
48008f9eb17SAlex Crichton             (&content_10k, 28, None, false),
48108f9eb17SAlex Crichton             (&content_10k, 27, None, false),
48208f9eb17SAlex Crichton             (&content_1k, 26, Some(18), true),
48308f9eb17SAlex Crichton             (&content_5k, 25, None, true),
48408f9eb17SAlex Crichton             (&content_1k, 24, None, true),
48508f9eb17SAlex Crichton         ],
48608f9eb17SAlex Crichton         &[
48708f9eb17SAlex Crichton             (&content_10k, 29, Some(19), true),
48808f9eb17SAlex Crichton             (&content_10k, 28, Some(18), true),
48908f9eb17SAlex Crichton             (&content_10k, 27, None, false),
49008f9eb17SAlex Crichton             (&content_1k, 26, Some(17), true),
49108f9eb17SAlex Crichton             (&content_5k, 25, None, false),
49208f9eb17SAlex Crichton             (&content_1k, 24, None, false),
49308f9eb17SAlex Crichton         ],
49408f9eb17SAlex Crichton         &[
49508f9eb17SAlex Crichton             (&content_10k, 29, Some(19), true),
49608f9eb17SAlex Crichton             (&content_10k, 28, None, false),
49708f9eb17SAlex Crichton             (&content_1k, 27, None, false),
49808f9eb17SAlex Crichton             (&content_5k, 26, Some(18), true),
49908f9eb17SAlex Crichton             (&content_1k, 25, None, false),
50008f9eb17SAlex Crichton             (&content_10k, 24, None, false),
50108f9eb17SAlex Crichton         ],
50208f9eb17SAlex Crichton     ];
50308f9eb17SAlex Crichton 
50408f9eb17SAlex Crichton     for mods in &scenarios {
50508f9eb17SAlex Crichton         let filenames = (0..mods.len())
50608f9eb17SAlex Crichton             .map(|i| {
50708f9eb17SAlex Crichton                 (
508*a0442ea0SHamir Mahal                     mods_files_dir.join(format!("mod-{i}")),
509*a0442ea0SHamir Mahal                     mods_files_dir.join(format!("mod-{i}.stats")),
51008f9eb17SAlex Crichton                 )
51108f9eb17SAlex Crichton             })
51208f9eb17SAlex Crichton             .collect::<Vec<_>>();
51308f9eb17SAlex Crichton 
51408f9eb17SAlex Crichton         for ((content, mod_secs_ago, create_stats, _), (mod_filename, stats_filename)) in
51508f9eb17SAlex Crichton             mods.iter().zip(filenames.iter())
51608f9eb17SAlex Crichton         {
51708f9eb17SAlex Crichton             create_file_with_mtime(
51808f9eb17SAlex Crichton                 mod_filename,
51908f9eb17SAlex Crichton                 content,
52008f9eb17SAlex Crichton                 "past",
52108f9eb17SAlex Crichton                 &Duration::from_secs(*mod_secs_ago),
52208f9eb17SAlex Crichton             );
52308f9eb17SAlex Crichton             if let Some(stats_secs_ago) = create_stats {
52408f9eb17SAlex Crichton                 create_file_with_mtime(
52508f9eb17SAlex Crichton                     stats_filename,
52608f9eb17SAlex Crichton                     "cleanup doesn't care",
52708f9eb17SAlex Crichton                     "past",
52808f9eb17SAlex Crichton                     &Duration::from_secs(*stats_secs_ago),
52908f9eb17SAlex Crichton                 );
53008f9eb17SAlex Crichton             }
53108f9eb17SAlex Crichton         }
53208f9eb17SAlex Crichton         create_file_with_mtime(
53308f9eb17SAlex Crichton             &orphaned_stats_file,
53408f9eb17SAlex Crichton             "cleanup doesn't care",
53508f9eb17SAlex Crichton             "past",
53608f9eb17SAlex Crichton             &Duration::from_secs(0),
53708f9eb17SAlex Crichton         );
53808f9eb17SAlex Crichton 
53908f9eb17SAlex Crichton         worker.on_cache_update_async(nonexistent_mod_file.clone());
54008f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
54108f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
54208f9eb17SAlex Crichton 
54308f9eb17SAlex Crichton         assert!(!orphaned_stats_file.exists());
54408f9eb17SAlex Crichton         for ((_, _, create_stats, alive), (mod_filename, stats_filename)) in
54508f9eb17SAlex Crichton             mods.iter().zip(filenames.iter())
54608f9eb17SAlex Crichton         {
54708f9eb17SAlex Crichton             assert_eq!(mod_filename.exists(), *alive);
54808f9eb17SAlex Crichton             assert_eq!(stats_filename.exists(), *alive && create_stats.is_some());
54908f9eb17SAlex Crichton 
55008f9eb17SAlex Crichton             // cleanup for next iteration
55108f9eb17SAlex Crichton             if *alive {
55208f9eb17SAlex Crichton                 fs::remove_file(&mod_filename).expect("Failed to remove a file");
55308f9eb17SAlex Crichton                 if create_stats.is_some() {
55408f9eb17SAlex Crichton                     fs::remove_file(&stats_filename).expect("Failed to remove a file");
55508f9eb17SAlex Crichton                 }
55608f9eb17SAlex Crichton             }
55708f9eb17SAlex Crichton         }
55808f9eb17SAlex Crichton 
55908f9eb17SAlex Crichton         fs::remove_file(&worker_lock_file).expect("Failed to remove lock file");
56008f9eb17SAlex Crichton     }
56108f9eb17SAlex Crichton }
56208f9eb17SAlex Crichton 
56308f9eb17SAlex Crichton // clock drift should be applied to mod cache & stats, too
56408f9eb17SAlex Crichton // however, postpone deleting files to as late as possible
56508f9eb17SAlex Crichton #[test]
test_on_update_cleanup_future_files()56608f9eb17SAlex Crichton fn test_on_update_cleanup_future_files() {
56708f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
56808f9eb17SAlex Crichton     let cache_config = load_config!(
56908f9eb17SAlex Crichton         config_path,
57008f9eb17SAlex Crichton         "[cache]\n\
57108c7359fSAlex Crichton          directory = '{cache_dir}'\n\
57208f9eb17SAlex Crichton          worker-event-queue-size = '16'\n\
57308f9eb17SAlex Crichton          allowed-clock-drift-for-files-from-future = '1d'\n\
57408f9eb17SAlex Crichton          file-count-soft-limit = '3'\n\
57508f9eb17SAlex Crichton          files-total-size-soft-limit = '1M'\n\
57608f9eb17SAlex Crichton          file-count-limit-percent-if-deleting = '70%'\n\
57708f9eb17SAlex Crichton          files-total-size-limit-percent-if-deleting = '70%'",
57808f9eb17SAlex Crichton         cache_dir
57908f9eb17SAlex Crichton     );
5801d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
58108f9eb17SAlex Crichton     let content_1k = "a".repeat(1_000);
58208f9eb17SAlex Crichton 
58308f9eb17SAlex Crichton     let mods_files_dir = cache_dir.join("target-triple").join("compiler-version");
58408f9eb17SAlex Crichton     fs::create_dir_all(&mods_files_dir).expect("Failed to create directories");
58508f9eb17SAlex Crichton     let nonexistent_mod_file = cache_dir.join("nonexistent-mod");
58608f9eb17SAlex Crichton     // the below created by the worker if it cleans up
58708f9eb17SAlex Crichton     let worker_lock_file = cache_dir.join(format!(".cleanup.wip-{}", process::id()));
58808f9eb17SAlex Crichton 
58908f9eb17SAlex Crichton     let scenarios: [&[_]; 5] = [
5900e9121daSFrankReh         // NOT cleaning up, everything is ok
59108f9eb17SAlex Crichton         &[
59208f9eb17SAlex Crichton             (Duration::from_secs(0), None, true),
59308f9eb17SAlex Crichton             (Duration::from_secs(24 * 60 * 60), None, true),
59408f9eb17SAlex Crichton         ],
5950e9121daSFrankReh         // NOT cleaning up, everything is ok
59608f9eb17SAlex Crichton         &[
59708f9eb17SAlex Crichton             (Duration::from_secs(0), None, true),
59808f9eb17SAlex Crichton             (Duration::from_secs(24 * 60 * 60 + 1), None, true),
59908f9eb17SAlex Crichton         ],
60008f9eb17SAlex Crichton         // cleaning up, removing files from oldest
60108f9eb17SAlex Crichton         &[
60208f9eb17SAlex Crichton             (Duration::from_secs(0), None, false),
60308f9eb17SAlex Crichton             (Duration::from_secs(24 * 60 * 60), None, true),
60408f9eb17SAlex Crichton             (Duration::from_secs(1), None, false),
60508f9eb17SAlex Crichton             (Duration::from_secs(2), None, true),
60608f9eb17SAlex Crichton         ],
60708f9eb17SAlex Crichton         // cleaning up, removing files from oldest; deleting file from far future
60808f9eb17SAlex Crichton         &[
60908f9eb17SAlex Crichton             (Duration::from_secs(0), None, false),
61008f9eb17SAlex Crichton             (Duration::from_secs(1), None, true),
61108f9eb17SAlex Crichton             (Duration::from_secs(24 * 60 * 60 + 1), None, false),
61208f9eb17SAlex Crichton             (Duration::from_secs(2), None, true),
61308f9eb17SAlex Crichton         ],
61408f9eb17SAlex Crichton         // cleaning up, removing files from oldest; file from far future should have .stats from +-now => it's a legitimate file
61508f9eb17SAlex Crichton         &[
61608f9eb17SAlex Crichton             (Duration::from_secs(0), None, false),
61708f9eb17SAlex Crichton             (Duration::from_secs(1), None, false),
61808f9eb17SAlex Crichton             (
61908f9eb17SAlex Crichton                 Duration::from_secs(24 * 60 * 60 + 1),
62008f9eb17SAlex Crichton                 Some(Duration::from_secs(3)),
62108f9eb17SAlex Crichton                 true,
62208f9eb17SAlex Crichton             ),
62308f9eb17SAlex Crichton             (Duration::from_secs(2), None, true),
62408f9eb17SAlex Crichton         ],
62508f9eb17SAlex Crichton     ];
62608f9eb17SAlex Crichton 
62708f9eb17SAlex Crichton     for mods in &scenarios {
62808f9eb17SAlex Crichton         let filenames = (0..mods.len())
62908f9eb17SAlex Crichton             .map(|i| {
63008f9eb17SAlex Crichton                 (
631*a0442ea0SHamir Mahal                     mods_files_dir.join(format!("mod-{i}")),
632*a0442ea0SHamir Mahal                     mods_files_dir.join(format!("mod-{i}.stats")),
63308f9eb17SAlex Crichton                 )
63408f9eb17SAlex Crichton             })
63508f9eb17SAlex Crichton             .collect::<Vec<_>>();
63608f9eb17SAlex Crichton 
63708f9eb17SAlex Crichton         for ((duration, opt_stats_duration, _), (mod_filename, stats_filename)) in
63808f9eb17SAlex Crichton             mods.iter().zip(filenames.iter())
63908f9eb17SAlex Crichton         {
64008f9eb17SAlex Crichton             create_file_with_mtime(mod_filename, &content_1k, "future", duration);
64108f9eb17SAlex Crichton             if let Some(stats_duration) = opt_stats_duration {
64208f9eb17SAlex Crichton                 create_file_with_mtime(stats_filename, "", "future", stats_duration);
64308f9eb17SAlex Crichton             }
64408f9eb17SAlex Crichton         }
64508f9eb17SAlex Crichton 
64608f9eb17SAlex Crichton         worker.on_cache_update_async(nonexistent_mod_file.clone());
64708f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
64808f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
64908f9eb17SAlex Crichton 
65008f9eb17SAlex Crichton         for ((_, opt_stats_duration, alive), (mod_filename, stats_filename)) in
65108f9eb17SAlex Crichton             mods.iter().zip(filenames.iter())
65208f9eb17SAlex Crichton         {
65308f9eb17SAlex Crichton             assert_eq!(mod_filename.exists(), *alive);
65408f9eb17SAlex Crichton             assert_eq!(
65508f9eb17SAlex Crichton                 stats_filename.exists(),
65608f9eb17SAlex Crichton                 *alive && opt_stats_duration.is_some()
65708f9eb17SAlex Crichton             );
65808f9eb17SAlex Crichton             if *alive {
65908f9eb17SAlex Crichton                 fs::remove_file(mod_filename).expect("Failed to remove a file");
66008f9eb17SAlex Crichton                 if opt_stats_duration.is_some() {
66108f9eb17SAlex Crichton                     fs::remove_file(stats_filename).expect("Failed to remove a file");
66208f9eb17SAlex Crichton                 }
66308f9eb17SAlex Crichton             }
66408f9eb17SAlex Crichton         }
66508f9eb17SAlex Crichton 
66608f9eb17SAlex Crichton         fs::remove_file(&worker_lock_file).expect("Failed to remove lock file");
66708f9eb17SAlex Crichton     }
66808f9eb17SAlex Crichton }
66908f9eb17SAlex Crichton 
67008f9eb17SAlex Crichton // this tests if worker triggered cleanup or not when some cleanup lock/certificate was out there
67108f9eb17SAlex Crichton #[test]
test_on_update_cleanup_self_lock()67208f9eb17SAlex Crichton fn test_on_update_cleanup_self_lock() {
67308f9eb17SAlex Crichton     let (_tempdir, cache_dir, config_path) = test_prolog();
67408f9eb17SAlex Crichton     let cache_config = load_config!(
67508f9eb17SAlex Crichton         config_path,
67608f9eb17SAlex Crichton         "[cache]\n\
67708c7359fSAlex Crichton          directory = '{cache_dir}'\n\
67808f9eb17SAlex Crichton          worker-event-queue-size = '16'\n\
67908f9eb17SAlex Crichton          cleanup-interval = '30m'\n\
68008f9eb17SAlex Crichton          allowed-clock-drift-for-files-from-future = '1d'",
68108f9eb17SAlex Crichton         cache_dir
68208f9eb17SAlex Crichton     );
6831d4766f3SQuentin Gliech     let worker = Worker::start_new(&cache_config);
68408f9eb17SAlex Crichton 
68508f9eb17SAlex Crichton     let mod_file = cache_dir.join("some-mod");
68608f9eb17SAlex Crichton     let trash_file = cache_dir.join("trash-file.txt");
68708f9eb17SAlex Crichton 
68808f9eb17SAlex Crichton     let lock_file = cache_dir.join(".cleanup.wip-lock");
68908f9eb17SAlex Crichton     // the below created by the worker if it cleans up
69008f9eb17SAlex Crichton     let worker_lock_file = cache_dir.join(format!(".cleanup.wip-{}", process::id()));
69108f9eb17SAlex Crichton 
69208f9eb17SAlex Crichton     let scenarios = [
69308f9eb17SAlex Crichton         // valid lock
69408f9eb17SAlex Crichton         (true, "past", Duration::from_secs(30 * 60 - 1)),
69508f9eb17SAlex Crichton         // valid future lock
69608f9eb17SAlex Crichton         (true, "future", Duration::from_secs(24 * 60 * 60)),
69708f9eb17SAlex Crichton         // expired lock
69808f9eb17SAlex Crichton         (false, "past", Duration::from_secs(30 * 60)),
69908f9eb17SAlex Crichton         // expired future lock
70008f9eb17SAlex Crichton         (false, "future", Duration::from_secs(24 * 60 * 60 + 1)),
70108f9eb17SAlex Crichton     ];
70208f9eb17SAlex Crichton 
70308f9eb17SAlex Crichton     for (lock_valid, duration_sign, duration) in &scenarios {
70408f9eb17SAlex Crichton         create_file_with_mtime(
70508f9eb17SAlex Crichton             &trash_file,
70608f9eb17SAlex Crichton             "with trash content",
70708f9eb17SAlex Crichton             "future",
70808f9eb17SAlex Crichton             &Duration::from_secs(0),
70908f9eb17SAlex Crichton         );
71008f9eb17SAlex Crichton         create_file_with_mtime(&lock_file, "", duration_sign, &duration);
71108f9eb17SAlex Crichton 
71208f9eb17SAlex Crichton         worker.on_cache_update_async(mod_file.clone());
71308f9eb17SAlex Crichton         worker.wait_for_all_events_handled();
71408f9eb17SAlex Crichton         assert_eq!(worker.events_dropped(), 0);
71508f9eb17SAlex Crichton 
71608f9eb17SAlex Crichton         assert_eq!(trash_file.exists(), *lock_valid);
71708f9eb17SAlex Crichton         assert_eq!(lock_file.exists(), *lock_valid);
71808f9eb17SAlex Crichton         if *lock_valid {
71908f9eb17SAlex Crichton             assert!(!worker_lock_file.exists());
72008f9eb17SAlex Crichton         } else {
72108f9eb17SAlex Crichton             fs::remove_file(&worker_lock_file).expect("Failed to remove lock file");
72208f9eb17SAlex Crichton         }
72308f9eb17SAlex Crichton     }
72408f9eb17SAlex Crichton }
72508f9eb17SAlex Crichton 
create_file_with_mtime(filename: &Path, contents: &str, offset_sign: &str, offset: &Duration)72608f9eb17SAlex Crichton fn create_file_with_mtime(filename: &Path, contents: &str, offset_sign: &str, offset: &Duration) {
72708f9eb17SAlex Crichton     fs::write(filename, contents).expect("Failed to create a file");
72808f9eb17SAlex Crichton     let mtime = match offset_sign {
72908f9eb17SAlex Crichton         "past" => system_time_stub::NOW
73008f9eb17SAlex Crichton             .checked_sub(*offset)
73108f9eb17SAlex Crichton             .expect("Failed to calculate new mtime"),
73208f9eb17SAlex Crichton         "future" => system_time_stub::NOW
73308f9eb17SAlex Crichton             .checked_add(*offset)
73408f9eb17SAlex Crichton             .expect("Failed to calculate new mtime"),
73508f9eb17SAlex Crichton         _ => unreachable!(),
73608f9eb17SAlex Crichton     };
73708f9eb17SAlex Crichton     filetime::set_file_mtime(filename, mtime.into()).expect("Failed to set mtime");
73808f9eb17SAlex Crichton }
739