From 31f52d70ca09b785b953f13ced37fa1fc6b7279e Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Thu, 12 Feb 2026 22:25:59 +0100 Subject: [PATCH] tests/truncate: add regression tests for -s %SIZE round-up bug The old RoundUp formula `fsize + fsize % size` was incorrect. For example, `truncate -s %128K` on a 24696-byte file produced 49392 bytes instead of the correct 131072 bytes. This was incidentally fixed in commit fe9793331 ("truncate: eliminate duplicate stat() syscall") by replacing the formula with `fsize.checked_next_multiple_of(size)`. However, the existing test `test_round_up` used values (fsize=10, size=4) where both the buggy and correct formulas give the same result (12), so the bug was never caught by the test suite. Add unit and integration tests that would have caught this bug: - File smaller than rounding unit (the reported scenario) - File larger than rounding unit but not aligned - File already aligned to the rounding unit - Division by zero (RoundUp with size=0) Link: https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/48210 Signed-off-by: Vincent Jardin --- src/uu/truncate/src/truncate.rs | 26 ++++++++++++++++++++++++ tests/by-util/test_truncate.rs | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index 997916b24a8..4461a4db280 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -363,5 +363,31 @@ mod tests { assert_eq!(TruncateMode::RoundUp(8).to_size(10), Some(16)); assert_eq!(TruncateMode::RoundUp(8).to_size(16), Some(16)); assert_eq!(TruncateMode::RoundDown(0).to_size(123), None); + assert_eq!(TruncateMode::RoundUp(0).to_size(123), None); + } + + #[test] + fn test_round_up_when_file_smaller_than_size() { + // fsize < size: must round up to size itself + assert_eq!( + TruncateMode::RoundUp(131_072).to_size(24_696), + Some(131_072) + ); + assert_eq!(TruncateMode::RoundUp(4096).to_size(1), Some(4096)); + assert_eq!(TruncateMode::RoundUp(100).to_size(50), Some(100)); + } + + #[test] + fn test_round_up_already_aligned() { + assert_eq!(TruncateMode::RoundUp(4096).to_size(0), Some(0)); + assert_eq!(TruncateMode::RoundUp(4096).to_size(4096), Some(4096)); + assert_eq!(TruncateMode::RoundUp(4096).to_size(8192), Some(8192)); + } + + #[test] + fn test_round_up_not_aligned() { + // fsize > size but not a multiple: must round up to next multiple + assert_eq!(TruncateMode::RoundUp(4096).to_size(5000), Some(8192)); + assert_eq!(TruncateMode::RoundUp(8).to_size(13), Some(16)); } } diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index 63c4442bd55..3057dcfbe68 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -173,6 +173,42 @@ fn test_round_up() { assert_eq!(expected, actual, "expected '{expected}' got '{actual}'"); } +#[test] +fn test_round_up_file_smaller_than_size() { + let expected = 4096; + let (at, mut ucmd) = at_and_ucmd!(); + let mut file = at.make_file(FILE2); + file.write_all(b"1234567890").unwrap(); + ucmd.args(&["--size", "%4K", FILE2]).succeeds(); + file.seek(SeekFrom::End(0)).unwrap(); + let actual = file.stream_position().unwrap(); + assert_eq!(expected, actual); +} + +#[test] +fn test_round_up_unaligned() { + let expected = 16; + let (at, mut ucmd) = at_and_ucmd!(); + let mut file = at.make_file(FILE2); + file.write_all(b"1234567890123").unwrap(); + ucmd.args(&["--size", "%8", FILE2]).succeeds(); + file.seek(SeekFrom::End(0)).unwrap(); + let actual = file.stream_position().unwrap(); + assert_eq!(expected, actual); +} + +#[test] +fn test_round_up_already_aligned() { + let expected = 8; + let (at, mut ucmd) = at_and_ucmd!(); + let mut file = at.make_file(FILE2); + file.write_all(b"12345678").unwrap(); + ucmd.args(&["--size", "%4", FILE2]).succeeds(); + file.seek(SeekFrom::End(0)).unwrap(); + let actual = file.stream_position().unwrap(); + assert_eq!(expected, actual); +} + #[test] fn test_size_and_reference() { let expected = 15;