|
1 | 1 | use crate::utils::{archive, setup}; |
2 | 2 | use clap::Parser; |
3 | 3 | use portable_network_archive::cli; |
4 | | -use std::{collections::HashSet, fs, thread, time}; |
| 4 | +use std::{ |
| 5 | + collections::HashSet, |
| 6 | + fs, thread, |
| 7 | + time::{Duration, SystemTime}, |
| 8 | +}; |
5 | 9 |
|
6 | 10 | /// Precondition: Create an archive with an older file, then prepare reference and newer files. |
7 | 11 | /// Action: Run `pna append` with `--newer-ctime-than reference.txt`, specifying older, reference, and newer files. |
8 | | -/// Expectation: Files with ctime >= reference are appended (reference and newer); older is not re-added. |
| 12 | +/// Expectation: Files with ctime > reference are appended (newer only); older and reference are not re-added. |
9 | 13 | /// Note: This test requires filesystem support for creation time (birth time). |
10 | 14 | #[test] |
11 | 15 | fn append_with_newer_ctime_than() { |
@@ -40,16 +44,21 @@ fn append_with_newer_ctime_than() { |
40 | 44 | .unwrap(); |
41 | 45 |
|
42 | 46 | // Wait to ensure distinct ctime |
43 | | - thread::sleep(time::Duration::from_millis(10)); |
| 47 | + thread::sleep(Duration::from_millis(10)); |
44 | 48 |
|
45 | 49 | // Create the reference file |
46 | 50 | fs::write(reference_file, "reference time marker").unwrap(); |
47 | 51 |
|
48 | 52 | // Wait to ensure the next file has a newer ctime |
49 | | - thread::sleep(time::Duration::from_millis(10)); |
| 53 | + thread::sleep(Duration::from_millis(10)); |
50 | 54 |
|
51 | 55 | // Create the newer file |
52 | 56 | fs::write(newer_file, "newer file content").unwrap(); |
| 57 | + let reference_ctime = fs::metadata(reference_file).unwrap().created().unwrap(); |
| 58 | + if !ensure_ctime_order(older_file, newer_file, reference_ctime) { |
| 59 | + eprintln!("Skipping test: unable to produce strict ctime ordering on this filesystem"); |
| 60 | + return; |
| 61 | + } |
53 | 62 |
|
54 | 63 | // Append to the archive with the `--newer-ctime-than` option |
55 | 64 | cli::Cli::try_parse_from([ |
@@ -81,23 +90,55 @@ fn append_with_newer_ctime_than() { |
81 | 90 | "older file should be in archive from initial creation: {older_file}" |
82 | 91 | ); |
83 | 92 |
|
84 | | - // reference_file should be included (appended because ctime >= reference) |
| 93 | + // reference_file should NOT be included (ctime == reference) |
85 | 94 | assert!( |
86 | | - seen.contains(reference_file), |
87 | | - "reference file should be appended: {reference_file}" |
| 95 | + !seen.contains(reference_file), |
| 96 | + "reference file should NOT be appended: {reference_file}" |
88 | 97 | ); |
89 | 98 |
|
90 | | - // newer_file should be included (appended because ctime >= reference) |
| 99 | + // newer_file should be included (appended because ctime > reference) |
91 | 100 | assert!( |
92 | 101 | seen.contains(newer_file), |
93 | 102 | "newer file should be appended: {newer_file}" |
94 | 103 | ); |
95 | 104 |
|
96 | | - // Verify that exactly three entries exist |
| 105 | + // Verify that exactly two entries exist |
97 | 106 | assert_eq!( |
98 | 107 | seen.len(), |
99 | | - 3, |
100 | | - "Expected exactly 3 entries, but found {}: {seen:?}", |
| 108 | + 2, |
| 109 | + "Expected exactly 2 entries, but found {}: {seen:?}", |
101 | 110 | seen.len() |
102 | 111 | ); |
103 | 112 | } |
| 113 | + |
| 114 | +fn ensure_ctime_order(older: &str, newer: &str, reference: SystemTime) -> bool { |
| 115 | + if !confirm_ctime_older_than(older, reference) { |
| 116 | + return false; |
| 117 | + } |
| 118 | + wait_until_ctime_newer_than(newer, reference) |
| 119 | +} |
| 120 | + |
| 121 | +fn wait_until_ctime_newer_than(path: &str, baseline: SystemTime) -> bool { |
| 122 | + const MAX_ATTEMPTS: usize = 500; |
| 123 | + const SLEEP_MS: u64 = 10; |
| 124 | + for _ in 0..MAX_ATTEMPTS { |
| 125 | + if fs::metadata(path) |
| 126 | + .ok() |
| 127 | + .and_then(|meta| meta.created().ok()) |
| 128 | + .map(|ctime| ctime > baseline) |
| 129 | + .unwrap_or(false) |
| 130 | + { |
| 131 | + return true; |
| 132 | + } |
| 133 | + thread::sleep(Duration::from_millis(SLEEP_MS)); |
| 134 | + } |
| 135 | + false |
| 136 | +} |
| 137 | + |
| 138 | +fn confirm_ctime_older_than(path: &str, baseline: SystemTime) -> bool { |
| 139 | + fs::metadata(path) |
| 140 | + .ok() |
| 141 | + .and_then(|meta| meta.created().ok()) |
| 142 | + .map(|ctime| ctime < baseline) |
| 143 | + .unwrap_or(false) |
| 144 | +} |
0 commit comments