Skip to content

Commit b31e6f8

Browse files
oech3oech3
authored andcommitted
Merge branch 'main' into gtest-build-time
2 parents 277d6bf + 29ec8f3 commit b31e6f8

File tree

4 files changed

+195
-100
lines changed

4 files changed

+195
-100
lines changed

src/uu/factor/benches/factor_bench.rs

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -22,95 +22,6 @@ fn factor_multiple_u64s(bencher: Bencher, start_num: u64) {
2222
});
2323
}
2424

25-
/* Too much variance
26-
/// Benchmark multiple u128 digits
27-
#[divan::bench(args = [(18446744073709551616)])]
28-
fn factor_multiple_u128s(bencher: Bencher, start_num: u128) {
29-
bencher
30-
.with_inputs(|| {
31-
// this is a range of 1000 different u128 integers
32-
(start_num, start_num + 1000)
33-
})
34-
.bench_values(|(start_u128, end_u128)| {
35-
for u128_digit in start_u128..=end_u128 {
36-
black_box(run_util_function(uumain, &[&u128_digit.to_string()]));
37-
}
38-
});
39-
}
40-
*/
41-
42-
/* Too much variance
43-
/// Benchmark multiple > u128::MAX digits
44-
#[divan::bench]
45-
fn factor_multiple_big_uint(bencher: Bencher) {
46-
// max u128 value is 340_282_366_920_938_463_463_374_607_431_768_211_455
47-
bencher
48-
// this is a range of 3 different BigUints. The range is small due to
49-
// some BigUints being unable to be factorized into prime numbers properly
50-
.with_inputs(|| (768_211_459_u64, 768_211_461_u64))
51-
.bench_values(|(start_big_uint, end_big_uint)| {
52-
for digit in start_big_uint..=end_big_uint {
53-
let big_uint_str = format!("340282366920938463463374607431768211456{digit}");
54-
black_box(run_util_function(uumain, &[&big_uint_str]));
55-
}
56-
});
57-
}
58-
*/
59-
60-
#[divan::bench()]
61-
fn factor_table(bencher: Bencher) {
62-
#[cfg(target_os = "linux")]
63-
check_personality();
64-
65-
const INPUT_SIZE: usize = 128;
66-
67-
let inputs = {
68-
// Deterministic RNG; use an explicitly-named RNG to guarantee stability
69-
use rand::{RngCore, SeedableRng};
70-
const SEED: u64 = 0xdead_bebe_ea75_cafe; // spell-checker:disable-line
71-
let mut rng = rand::rngs::StdRng::seed_from_u64(SEED);
72-
73-
std::iter::repeat_with(move || {
74-
let mut array = [0u64; INPUT_SIZE];
75-
for item in &mut array {
76-
*item = rng.next_u64();
77-
}
78-
array
79-
})
80-
.take(10)
81-
.collect::<Vec<_>>()
82-
};
83-
84-
bencher.bench(|| {
85-
for a in &inputs {
86-
for n in a {
87-
divan::black_box(num_prime::nt_funcs::factors(*n, None));
88-
}
89-
}
90-
});
91-
}
92-
93-
#[cfg(target_os = "linux")]
94-
fn check_personality() {
95-
use std::fs;
96-
const ADDR_NO_RANDOMIZE: u64 = 0x0040000;
97-
const PERSONALITY_PATH: &str = "/proc/self/personality";
98-
99-
let p_string = fs::read_to_string(PERSONALITY_PATH)
100-
.unwrap_or_else(|_| panic!("Couldn't read '{PERSONALITY_PATH}'"))
101-
.strip_suffix('\n')
102-
.unwrap()
103-
.to_owned();
104-
105-
let personality = u64::from_str_radix(&p_string, 16)
106-
.unwrap_or_else(|_| panic!("Expected a hex value for personality, got '{p_string:?}'"));
107-
if personality & ADDR_NO_RANDOMIZE == 0 {
108-
eprintln!(
109-
"WARNING: Benchmarking with ASLR enabled (personality is {personality:x}), results might not be reproducible."
110-
);
111-
}
112-
}
113-
11425
fn main() {
11526
divan::main();
11627
}

src/uu/install/src/mode.rs

Lines changed: 142 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,25 @@ use uucore::mode;
99
use uucore::translate;
1010

1111
/// Takes a user-supplied string and tries to parse to u16 mode bitmask.
12+
/// Supports comma-separated mode strings like "ug+rwX,o+rX" (same as chmod).
1213
pub fn parse(mode_string: &str, considering_dir: bool, umask: u32) -> Result<u32, String> {
13-
if mode_string.chars().any(|c| c.is_ascii_digit()) {
14-
mode::parse_numeric(0, mode_string, considering_dir)
15-
} else {
16-
mode::parse_symbolic(0, mode_string, umask, considering_dir)
14+
// Split by commas and process each mode part sequentially
15+
let mut current_mode: u32 = 0;
16+
17+
for mode_part in mode_string.split(',') {
18+
let mode_part = mode_part.trim();
19+
if mode_part.is_empty() {
20+
continue;
21+
}
22+
23+
current_mode = if mode_part.chars().any(|c| c.is_ascii_digit()) {
24+
mode::parse_numeric(current_mode, mode_part, considering_dir)?
25+
} else {
26+
mode::parse_symbolic(current_mode, mode_part, umask, considering_dir)?
27+
};
1728
}
29+
30+
Ok(current_mode)
1831
}
1932

2033
/// chmod a file or directory on UNIX.
@@ -42,3 +55,128 @@ pub fn chmod(path: &Path, mode: u32) -> Result<(), ()> {
4255
// chmod on Windows only sets the readonly flag, which isn't even honored on directories
4356
Ok(())
4457
}
58+
59+
#[cfg(test)]
60+
#[cfg(not(windows))]
61+
mod tests {
62+
use super::parse;
63+
64+
#[test]
65+
fn test_parse_numeric_mode() {
66+
// Simple numeric mode
67+
assert_eq!(parse("644", false, 0).unwrap(), 0o644);
68+
assert_eq!(parse("755", false, 0).unwrap(), 0o755);
69+
assert_eq!(parse("777", false, 0).unwrap(), 0o777);
70+
assert_eq!(parse("600", false, 0).unwrap(), 0o600);
71+
}
72+
73+
#[test]
74+
fn test_parse_numeric_mode_with_operator() {
75+
// Numeric mode with + operator
76+
assert_eq!(parse("+100", false, 0).unwrap(), 0o100);
77+
assert_eq!(parse("+644", false, 0).unwrap(), 0o644);
78+
79+
// Numeric mode with - operator (starting from 0, so nothing to remove)
80+
assert_eq!(parse("-4", false, 0).unwrap(), 0);
81+
// But if we first set a mode, then remove bits
82+
assert_eq!(parse("644,-4", false, 0).unwrap(), 0o640);
83+
}
84+
85+
#[test]
86+
fn test_parse_symbolic_mode() {
87+
// Simple symbolic modes
88+
assert_eq!(parse("u+x", false, 0).unwrap(), 0o100);
89+
assert_eq!(parse("g+w", false, 0).unwrap(), 0o020);
90+
assert_eq!(parse("o+r", false, 0).unwrap(), 0o004);
91+
assert_eq!(parse("a+x", false, 0).unwrap(), 0o111);
92+
}
93+
94+
#[test]
95+
fn test_parse_symbolic_mode_multiple_permissions() {
96+
// Multiple permissions in one mode
97+
assert_eq!(parse("u+rw", false, 0).unwrap(), 0o600);
98+
assert_eq!(parse("ug+rwx", false, 0).unwrap(), 0o770);
99+
assert_eq!(parse("a+rwx", false, 0).unwrap(), 0o777);
100+
}
101+
102+
#[test]
103+
fn test_parse_comma_separated_modes() {
104+
// Comma-separated mode strings (as mentioned in the doc comment)
105+
assert_eq!(parse("ug+rwX,o+rX", false, 0).unwrap(), 0o664);
106+
assert_eq!(parse("u+rwx,g+rx,o+r", false, 0).unwrap(), 0o754);
107+
assert_eq!(parse("u+w,g+w,o+w", false, 0).unwrap(), 0o222);
108+
}
109+
110+
#[test]
111+
fn test_parse_comma_separated_with_spaces() {
112+
// Comma-separated with spaces (should be trimmed)
113+
assert_eq!(parse("u+rw, g+rw, o+r", false, 0).unwrap(), 0o664);
114+
assert_eq!(parse(" u+x , g+x ", false, 0).unwrap(), 0o110);
115+
}
116+
117+
#[test]
118+
fn test_parse_mixed_numeric_and_symbolic() {
119+
// Mix of numeric and symbolic modes
120+
assert_eq!(parse("644,u+x", false, 0).unwrap(), 0o744);
121+
assert_eq!(parse("u+rw,755", false, 0).unwrap(), 0o755);
122+
}
123+
124+
#[test]
125+
fn test_parse_empty_string() {
126+
// Empty string should return 0
127+
assert_eq!(parse("", false, 0).unwrap(), 0);
128+
assert_eq!(parse(" ", false, 0).unwrap(), 0);
129+
assert_eq!(parse(",,", false, 0).unwrap(), 0);
130+
}
131+
132+
#[test]
133+
fn test_parse_with_umask() {
134+
// Test with umask (affects symbolic modes when no level is specified)
135+
let umask = 0o022;
136+
assert_eq!(parse("+w", false, umask).unwrap(), 0o200);
137+
// The umask should be respected for symbolic modes without explicit level
138+
}
139+
140+
#[test]
141+
fn test_parse_considering_dir() {
142+
// Test directory vs file mode differences
143+
// For directories, X (capital X) should add execute permission
144+
assert_eq!(parse("a+X", true, 0).unwrap(), 0o111);
145+
// For files without execute, X should not add execute
146+
assert_eq!(parse("a+X", false, 0).unwrap(), 0o000);
147+
148+
// Numeric modes for directories preserve setuid/setgid bits
149+
assert_eq!(parse("755", true, 0).unwrap(), 0o755);
150+
}
151+
152+
#[test]
153+
fn test_parse_invalid_modes() {
154+
// Invalid numeric mode (too large)
155+
assert!(parse("10000", false, 0).is_err());
156+
157+
// Invalid operator
158+
assert!(parse("u*rw", false, 0).is_err());
159+
160+
// Invalid symbolic mode
161+
assert!(parse("invalid", false, 0).is_err());
162+
}
163+
164+
#[test]
165+
fn test_parse_complex_combinations() {
166+
// Complex real-world examples
167+
assert_eq!(parse("u=rwx,g=rx,o=r", false, 0).unwrap(), 0o754);
168+
// To test removal, we need to first set permissions, then remove them
169+
assert_eq!(parse("644,a-w", false, 0).unwrap(), 0o444);
170+
assert_eq!(parse("644,g-r", false, 0).unwrap(), 0o604);
171+
}
172+
173+
#[test]
174+
fn test_parse_sequential_application() {
175+
// Test that comma-separated modes are applied sequentially
176+
// First set to 644, then add execute for user
177+
assert_eq!(parse("644,u+x", false, 0).unwrap(), 0o744);
178+
179+
// First add user write, then set to 755 (should override)
180+
assert_eq!(parse("u+w,755", false, 0).unwrap(), 0o755);
181+
}
182+
}

tests/by-util/test_install.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,52 @@ fn test_install_mode_symbolic() {
243243
assert_eq!(0o100_003_u32, PermissionsExt::mode(&permissions));
244244
}
245245

246+
#[test]
247+
fn test_install_mode_comma_separated() {
248+
let (at, mut ucmd) = at_and_ucmd!();
249+
let dir = "target_dir";
250+
let file = "source_file";
251+
// Test comma-separated mode like chmod: ug+rwX,o+rX
252+
let mode_arg = "--mode=ug+rwX,o+rX";
253+
254+
at.touch(file);
255+
at.mkdir(dir);
256+
ucmd.arg(file).arg(dir).arg(mode_arg).succeeds().no_stderr();
257+
258+
let dest_file = &format!("{dir}/{file}");
259+
assert!(at.file_exists(file));
260+
assert!(at.file_exists(dest_file));
261+
let permissions = at.metadata(dest_file).permissions();
262+
// ug+rwX: For files, X only adds execute if file already has execute (it doesn't here, starting at 0)
263+
// So this adds rw to user and group = 0o660
264+
// o+rX: For files, X doesn't add execute, so this adds r to others = 0o004
265+
// Total: 0o664 for file (0o100_664)
266+
assert_eq!(0o100_664_u32, PermissionsExt::mode(&permissions));
267+
}
268+
269+
#[test]
270+
fn test_install_mode_comma_separated_directory() {
271+
let scene = TestScenario::new(util_name!());
272+
let at = &scene.fixtures;
273+
let dir = "test_dir";
274+
// Test comma-separated mode for directory creation: ug+rwX,o+rX
275+
let mode_arg = "--mode=ug+rwX,o+rX";
276+
277+
scene
278+
.ucmd()
279+
.arg("-d")
280+
.arg(dir)
281+
.arg(mode_arg)
282+
.succeeds()
283+
.no_stderr();
284+
285+
assert!(at.dir_exists(dir));
286+
let permissions = at.metadata(dir).permissions();
287+
// ug+rwX sets user and group to rwx (0o770), o+rX sets others to r-x (0o005)
288+
// Total: 0o775 for directory (0o040_775)
289+
assert_eq!(0o040_775_u32, PermissionsExt::mode(&permissions));
290+
}
291+
246292
#[test]
247293
fn test_install_mode_symbolic_ignore_umask() {
248294
let (at, mut ucmd) = at_and_ucmd!();

util/build-gnu.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,21 @@ fi
105105
cd -
106106

107107
[ "${SELINUX_ENABLED}" != 1 ] && export MULTICALL=y # Reduce time to build
108-
# Pass the feature flags to make, which will pass them to cargo
109-
"${MAKE}" PROFILE="${UU_MAKE_PROFILE}" CARGOFLAGS="${CARGO_FEATURE_FLAGS}"
108+
# The GNU tests rename install to ginstall
109+
"${MAKE}" UTILS=install MULTICALL=n PROFILE="${UU_MAKE_PROFILE}" CARGOFLAGS="${CARGO_FEATURE_FLAGS}" && cp "${UU_BUILD_DIR}/install" "${UU_BUILD_DIR}/ginstall"
110+
"${MAKE}" SKIP_UTILS=install PROFILE="${UU_MAKE_PROFILE}" CARGOFLAGS="${CARGO_FEATURE_FLAGS}"
110111
if [ "${MULTICALL}" = y ]; then
111-
for b in $(./coreutils --list)
112-
do cp -vf "${UU_BUILD_DIR}"/coreutils "${UU_BUILD_DIR}/${b}"
112+
for b in $("${UU_BUILD_DIR}"/coreutils --list)
113+
do cp -v --remove-destination "${UU_BUILD_DIR}"/coreutils "${UU_BUILD_DIR}/${b}"
113114
done
114115
else
115-
cp "${UU_BUILD_DIR}"/test "${UU_BUILD_DIR}"/test/'['
116+
ln -sf "${UU_BUILD_DIR}"/test "${UU_BUILD_DIR}"/'['
116117
for b in {b2,md5}sum sha{1,224,256,384,512}sum
117-
do cp -vf "${UU_BUILD_DIR}"/hashsum "${UU_BUILD_DIR}/${b}"
118+
do ln -sf "${UU_BUILD_DIR}"/hashsum "${UU_BUILD_DIR}/${b}"
118119
done
119120
fi
120121
# min test for SELinux
121122
[ "${SELINUX_ENABLED}" = 1 ] && touch g && "${UU_MAKE_PROFILE}"/stat -c%C g && rm g
122-
cp "${UU_BUILD_DIR}/install" "${UU_BUILD_DIR}/ginstall" # The GNU tests rename this script before running, to avoid confusion with the make target
123123

124124
##
125125

0 commit comments

Comments
 (0)