From 764eef59cd9e6b91c40df095777ea8b0a80ee130 Mon Sep 17 00:00:00 2001 From: Vincent JARDIN Date: Sat, 14 Feb 2026 15:02:24 +0100 Subject: [PATCH] tests/truncate: add regression tests for -s %SIZE round-up bug (#10901) 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 997916b24..4461a4db2 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 63c4442bd..3057dcfbe 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;