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