2023-08-21 10:49:27 +02:00
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
2021-06-11 17:24:12 +02:00
// spell-checker:ignore (words) nosuchgroup groupname
2021-05-30 00:10:54 -05:00
2025-08-08 15:26:19 +02:00
#[cfg(target_os = "linux" )]
use std ::os ::unix ::ffi ::OsStringExt ;
2023-06-04 14:55:16 +02:00
use uucore ::process ::getegid ;
2025-07-01 01:52:57 +02:00
use uutests ::{ at_and_ucmd , new_ucmd };
#[cfg(not(target_vendor = "apple" ))]
use uutests ::{ util ::TestScenario , util_name };
2016-08-21 17:05:05 +08:00
#[test]
fn test_invalid_option () {
2020-04-13 20:36:03 +02:00
new_ucmd! (). arg ( "-w" ). arg ( "/" ). fails ();
2016-08-21 17:05:05 +08:00
}
2022-09-10 18:38:14 +02:00
#[test]
fn test_invalid_arg () {
2025-03-01 15:29:11 +01:00
new_ucmd! (). arg ( "--definitely-invalid" ). fails_with_code ( 1 );
2022-09-10 18:38:14 +02:00
}
2022-02-09 13:08:28 -05:00
static DIR : & str = "/dev" ;
2016-08-21 17:05:05 +08:00
2021-06-11 17:24:12 +02:00
// we should always get both arguments, regardless of whether --reference was used
2021-05-19 22:38:51 -04:00
#[test]
fn test_help () {
new_ucmd! ()
. arg ( "--help" )
. succeeds ()
2022-09-29 15:29:32 +02:00
. stdout_contains ( "Arguments:" );
2021-05-19 22:38:51 -04:00
}
#[test]
fn test_help_ref () {
new_ucmd! ()
. arg ( "--help" )
. arg ( "--reference=ref_file" )
. succeeds ()
2022-09-29 15:29:32 +02:00
. stdout_contains ( "Arguments:" );
2021-05-19 22:38:51 -04:00
}
#[test]
fn test_ref_help () {
new_ucmd! ()
. arg ( "--reference=ref_file" )
. arg ( "--help" )
. succeeds ()
2022-09-29 15:29:32 +02:00
. stdout_contains ( "Arguments:" );
2021-05-19 22:38:51 -04:00
}
2016-08-21 17:05:05 +08:00
#[test]
fn test_invalid_group () {
2016-08-23 07:52:43 -04:00
new_ucmd! ()
2018-03-01 23:08:09 -08:00
. arg ( "__nosuchgroup__" )
2016-08-21 17:05:05 +08:00
. arg ( "/" )
. fails ()
2023-01-05 21:09:15 +01:00
. stderr_is ( "chgrp: invalid group: '__nosuchgroup__' \n " );
2016-08-21 17:05:05 +08:00
}
#[test]
2025-01-12 22:38:04 +01:00
fn test_error_1 () {
2023-06-04 14:55:16 +02:00
if getegid () != 0 {
2022-02-09 13:08:28 -05:00
new_ucmd! (). arg ( "bin" ). arg ( DIR ). fails (). stderr_contains (
// linux fails with "Operation not permitted (os error 1)"
// because of insufficient permissions,
// android fails with "Permission denied (os error 13)"
// because it can't resolve /proc (even though it can resolve /proc/self/)
"chgrp: changing group of '/dev': " ,
);
2018-03-01 23:08:09 -08:00
}
2016-08-21 17:05:05 +08:00
}
#[test]
fn test_fail_silently () {
2023-06-04 14:55:16 +02:00
if getegid () != 0 {
2022-04-02 10:47:37 +02:00
for opt in [ "-f" , "--silent" , "--quiet" , "--sil" , "--qui" ] {
2018-03-01 23:08:09 -08:00
new_ucmd! ()
. arg ( opt )
. arg ( "bin" )
. arg ( DIR )
. run ()
. fails_silently ();
}
2016-08-21 17:05:05 +08:00
}
}
#[test]
fn test_preserve_root () {
2016-08-28 19:55:41 +08:00
// It's weird that on OS X, `realpath /etc/..` returns '/private'
2024-03-03 08:26:43 +01:00
new_ucmd! ()
. arg ( "--preserve-root" )
. arg ( "-R" )
. arg ( "bin" )
. arg ( "/" )
. fails ()
. stderr_is ( "chgrp: it is dangerous to operate recursively on '/' \n chgrp: use --no-preserve-root to override this failsafe \n " );
2022-04-02 10:47:37 +02:00
for d in [
2022-02-09 13:08:28 -05:00
"/////dev///../../../../" ,
2020-04-13 20:36:03 +02:00
"../../../../../../../../../../../../../../" ,
"./../../../../../../../../../../../../../../" ,
] {
2024-03-03 08:26:43 +01:00
let expected_error = format! (
2025-04-07 22:56:21 -04:00
"chgrp: it is dangerous to operate recursively on ' {d} ' (same as '/') \n chgrp: use --no-preserve-root to override this failsafe \n " ,
2024-03-03 08:26:43 +01:00
);
2016-08-28 19:55:41 +08:00
new_ucmd! ()
. arg ( "--preserve-root" )
. arg ( "-R" )
2024-03-03 08:26:43 +01:00
. arg ( "bin" )
. arg ( d )
2016-08-28 19:55:41 +08:00
. fails ()
2024-03-03 08:26:43 +01:00
. stderr_is ( expected_error );
2016-08-28 19:55:41 +08:00
}
}
#[test]
fn test_preserve_root_symlink () {
let file = "test_chgrp_symlink2root" ;
2022-04-02 10:47:37 +02:00
for d in [
2020-04-13 20:36:03 +02:00
"/" ,
2024-03-03 08:26:43 +01:00
"//" ,
"///" ,
2022-02-09 13:08:28 -05:00
"////dev//../../../../" ,
2020-04-13 20:36:03 +02:00
"..//../../..//../..//../../../../../../../../" ,
".//../../../../../../..//../../../../../../../" ,
] {
2016-08-28 19:55:41 +08:00
let ( at , mut ucmd ) = at_and_ucmd! ();
2018-11-12 22:55:25 -06:00
at . symlink_file ( d , file );
2025-03-24 21:04:32 +01:00
let expected_error = "chgrp: it is dangerous to operate recursively on 'test_chgrp_symlink2root' (same as '/') \n chgrp: use --no-preserve-root to override this failsafe \n " ;
2016-08-28 19:55:41 +08:00
ucmd . arg ( "--preserve-root" )
. arg ( "-HR" )
2024-03-03 08:26:43 +01:00
. arg ( "bin" )
. arg ( file )
2016-08-28 19:55:41 +08:00
. fails ()
2024-03-03 08:26:43 +01:00
. stderr_is ( expected_error );
2016-08-28 19:55:41 +08:00
}
let ( at , mut ucmd ) = at_and_ucmd! ();
2022-02-09 13:08:28 -05:00
at . symlink_file ( "///dev" , file );
2016-08-28 19:55:41 +08:00
ucmd . arg ( "--preserve-root" )
. arg ( "-HR" )
2023-01-27 10:29:45 +01:00
. arg ( "bin" ). arg ( format! ( ".// {file} /..//..//../../" ))
2016-08-28 19:55:41 +08:00
. fails ()
2024-03-03 08:26:43 +01:00
. stderr_is ( "chgrp: it is dangerous to operate recursively on './/test_chgrp_symlink2root/..//..//../../' (same as '/') \n chgrp: use --no-preserve-root to override this failsafe \n " );
2016-08-28 19:55:41 +08:00
let ( at , mut ucmd ) = at_and_ucmd! ();
2022-02-09 13:08:28 -05:00
at . symlink_file ( "/" , "__root__" );
2016-08-28 19:55:41 +08:00
ucmd . arg ( "--preserve-root" )
2016-08-21 17:05:05 +08:00
. arg ( "-R" )
2022-02-09 13:08:28 -05:00
. arg ( "bin" ). arg ( "__root__/." )
2016-08-21 17:05:05 +08:00
. fails ()
2024-03-03 08:26:43 +01:00
. stderr_is ( "chgrp: it is dangerous to operate recursively on '__root__/.' (same as '/') \n chgrp: use --no-preserve-root to override this failsafe \n " );
}
#[test]
fn test_preserve_root_symlink_cwd_root () {
new_ucmd! ()
. current_dir ( "/" )
. arg ( "--preserve-root" )
. arg ( "-R" )
. arg ( "bin" ). arg ( "." )
. fails ()
. stderr_is ( "chgrp: it is dangerous to operate recursively on '.' (same as '/') \n chgrp: use --no-preserve-root to override this failsafe \n " );
new_ucmd! ()
. current_dir ( "/" )
. arg ( "--preserve-root" )
. arg ( "-R" )
. arg ( "bin" ). arg ( "/." )
. fails ()
. stderr_is ( "chgrp: it is dangerous to operate recursively on '/.' (same as '/') \n chgrp: use --no-preserve-root to override this failsafe \n " );
new_ucmd! ()
. current_dir ( "/" )
. arg ( "--preserve-root" )
. arg ( "-R" )
. arg ( "bin" ). arg ( ".." )
. fails ()
. stderr_is ( "chgrp: it is dangerous to operate recursively on '..' (same as '/') \n chgrp: use --no-preserve-root to override this failsafe \n " );
new_ucmd! ()
. current_dir ( "/" )
. arg ( "--preserve-root" )
. arg ( "-R" )
. arg ( "bin" ). arg ( "/.." )
. fails ()
. stderr_is ( "chgrp: it is dangerous to operate recursively on '/..' (same as '/') \n chgrp: use --no-preserve-root to override this failsafe \n " );
new_ucmd! ()
. current_dir ( "/" )
. arg ( "--preserve-root" )
. arg ( "-R" )
. arg ( "bin" )
. arg ( "..." )
. fails ()
. stderr_is ( "chgrp: cannot access '...': No such file or directory \n " );
2016-08-21 17:05:05 +08:00
}
#[test]
#[cfg(target_os = "linux" )]
fn test_reference () {
2019-12-25 00:42:11 -06:00
// skip for root or MS-WSL
// * MS-WSL is bugged (as of 2019-12-25), allowing non-root accounts su-level privileges for `chgrp`
// * for MS-WSL, succeeds and stdout == 'group of /etc retained as root'
2023-06-04 14:55:16 +02:00
if ! ( getegid () == 0 || uucore ::os ::is_wsl_1 ()) {
2018-03-01 23:08:09 -08:00
new_ucmd! ()
. arg ( "-v" )
. arg ( "--reference=/etc/passwd" )
. arg ( "/etc" )
. fails ()
2023-01-05 21:09:15 +01:00
. stderr_is ( "chgrp: changing group of '/etc': Operation not permitted (os error 1) \n failed to change group of '/etc' from root to root \n " );
2018-03-01 23:08:09 -08:00
}
2016-08-21 17:05:05 +08:00
}
#[test]
2021-03-17 21:32:34 -04:00
#[cfg(target_vendor = "apple" )]
2016-08-21 17:05:05 +08:00
fn test_reference () {
2016-08-23 07:52:43 -04:00
new_ucmd! ()
2016-08-21 17:05:05 +08:00
. arg ( "-v" )
2021-05-19 22:38:51 -04:00
. arg ( "--reference=ref_file" )
2016-08-21 17:05:05 +08:00
. arg ( "/etc" )
2021-05-19 22:38:51 -04:00
. fails ()
// group name can differ, so just check the first part of the message
. stderr_contains ( "chgrp: changing group of '/etc': Operation not permitted (os error 1) \n failed to change group of '/etc' from " );
}
#[test]
2022-02-09 13:08:28 -05:00
#[cfg(any(target_os = "linux" , target_os = "android" , target_vendor = "apple" ))]
2021-05-19 22:38:51 -04:00
fn test_reference_multi_no_equal () {
new_ucmd! ()
. arg ( "-v" )
. arg ( "--reference" )
. arg ( "ref_file" )
. arg ( "file1" )
. arg ( "file2" )
. succeeds ()
. stderr_contains ( "chgrp: group of 'file1' retained as " )
. stderr_contains ( " \n chgrp: group of 'file2' retained as " );
}
#[test]
2022-02-09 13:08:28 -05:00
#[cfg(any(target_os = "linux" , target_os = "android" , target_vendor = "apple" ))]
2021-05-19 22:38:51 -04:00
fn test_reference_last () {
new_ucmd! ()
. arg ( "-v" )
. arg ( "file1" )
. arg ( "file2" )
. arg ( "file3" )
. arg ( "--reference" )
. arg ( "ref_file" )
. succeeds ()
. stderr_contains ( "chgrp: group of 'file1' retained as " )
. stderr_contains ( " \n chgrp: group of 'file2' retained as " )
. stderr_contains ( " \n chgrp: group of 'file3' retained as " );
}
#[test]
fn test_missing_files () {
new_ucmd! ()
. arg ( "-v" )
. arg ( "groupname" )
. fails ()
. stderr_contains (
2023-03-08 15:06:07 +01:00
"error: the following required arguments were not provided: \n <FILE>... \n " ,
2021-05-19 22:38:51 -04:00
);
2016-08-21 17:05:05 +08:00
}
#[test]
#[cfg(target_os = "linux" )]
fn test_big_p () {
2023-06-04 14:55:16 +02:00
if getegid () != 0 {
2018-03-01 23:08:09 -08:00
new_ucmd! ()
. arg ( "-RP" )
. arg ( "bin" )
. arg ( "/proc/self/cwd" )
. fails ()
2021-05-19 22:38:51 -04:00
. stderr_contains (
2020-04-13 20:36:03 +02:00
"chgrp: changing group of '/proc/self/cwd': Operation not permitted (os error 1) \n " ,
);
2018-03-01 23:08:09 -08:00
}
2016-08-21 17:05:05 +08:00
}
#[test]
2022-02-09 13:08:28 -05:00
#[cfg(any(target_os = "linux" , target_os = "android" ))]
2016-08-21 17:05:05 +08:00
fn test_big_h () {
2023-06-04 14:55:16 +02:00
if getegid () != 0 {
2020-04-13 20:36:03 +02:00
assert! (
new_ucmd! ()
. arg ( "-RH" )
. arg ( "bin" )
. arg ( "/proc/self/fd" )
. fails ()
2021-04-05 23:03:43 +03:00
. stderr_str ()
2020-04-13 20:36:03 +02:00
. lines ()
. fold ( 0 , | acc , _ | acc + 1 )
> 1
);
2018-03-01 23:08:09 -08:00
}
2016-08-21 17:05:05 +08:00
}
2021-08-09 01:33:02 +02:00
#[test]
2021-09-01 17:34:40 +02:00
#[cfg(not(target_vendor = "apple" ))]
2021-08-09 01:33:02 +02:00
fn basic_succeeds () {
let ( at , mut ucmd ) = at_and_ucmd! ();
let one_group = nix ::unistd ::getgroups (). unwrap ();
// if there are no groups we can't run this test.
if let Some ( group ) = one_group . first () {
at . touch ( "f1" );
ucmd . arg ( group . as_raw (). to_string ())
. arg ( "f1" )
. succeeds ()
. no_stdout ()
. no_stderr ();
}
}
2021-08-13 01:35:21 +02:00
2021-08-10 23:31:42 +02:00
#[test]
fn test_no_change () {
let ( at , mut ucmd ) = at_and_ucmd! ();
at . touch ( "file" );
ucmd . arg ( "" ). arg ( at . plus ( "file" )). succeeds ();
}
2021-09-01 17:34:40 +02:00
#[test]
#[cfg(not(target_vendor = "apple" ))]
fn test_permission_denied () {
use std ::os ::unix ::prelude ::PermissionsExt ;
if let Some ( group ) = nix ::unistd ::getgroups (). unwrap (). first () {
let ( at , mut ucmd ) = at_and_ucmd! ();
at . mkdir ( "dir" );
at . touch ( "dir/file" );
std ::fs ::set_permissions ( at . plus ( "dir" ), PermissionsExt ::from_mode ( 0o0000 )). unwrap ();
ucmd . arg ( "-R" )
. arg ( group . as_raw (). to_string ())
. arg ( "dir" )
. fails ()
2023-01-05 21:09:15 +01:00
. stderr_only ( "chgrp: cannot access 'dir': Permission denied \n " );
2021-09-01 17:34:40 +02:00
}
}
#[test]
#[cfg(not(target_vendor = "apple" ))]
fn test_subdir_permission_denied () {
use std ::os ::unix ::prelude ::PermissionsExt ;
if let Some ( group ) = nix ::unistd ::getgroups (). unwrap (). first () {
let ( at , mut ucmd ) = at_and_ucmd! ();
at . mkdir ( "dir" );
at . mkdir ( "dir/subdir" );
at . touch ( "dir/subdir/file" );
std ::fs ::set_permissions ( at . plus ( "dir/subdir" ), PermissionsExt ::from_mode ( 0o0000 )). unwrap ();
ucmd . arg ( "-R" )
. arg ( group . as_raw (). to_string ())
. arg ( "dir" )
. fails ()
2023-01-05 21:09:15 +01:00
. stderr_only ( "chgrp: cannot access 'dir/subdir': Permission denied \n " );
2021-09-01 17:34:40 +02:00
}
}
2021-09-02 22:31:49 +02:00
#[test]
#[cfg(not(target_vendor = "apple" ))]
fn test_traverse_symlinks () {
use std ::os ::unix ::prelude ::MetadataExt ;
let groups = nix ::unistd ::getgroups (). unwrap ();
if groups . len () < 2 {
return ;
}
let ( first_group , second_group ) = ( groups [ 0 ], groups [ 1 ]);
2022-04-02 10:47:37 +02:00
for ( args , traverse_first , traverse_second ) in [
2021-09-02 22:31:49 +02:00
( & [][ .. ] as & [ & str ], false , false ),
( & [ "-H" ][ .. ], true , false ),
( & [ "-P" ][ .. ], false , false ),
( & [ "-L" ][ .. ], true , true ),
] {
2025-07-01 01:52:57 +02:00
let scenario = TestScenario ::new ( util_name! ());
2021-09-02 22:31:49 +02:00
let ( at , mut ucmd ) = ( scenario . fixtures . clone (), scenario . ucmd ());
at . mkdir ( "dir" );
at . mkdir ( "dir2" );
at . touch ( "dir2/file" );
at . mkdir ( "dir3" );
at . touch ( "dir3/file" );
at . symlink_dir ( "dir2" , "dir/dir2_ln" );
at . symlink_dir ( "dir3" , "dir3_ln" );
scenario
. ccmd ( "chgrp" )
. arg ( first_group . to_string ())
. arg ( "dir2/file" )
. arg ( "dir3/file" )
. succeeds ();
2025-04-08 13:25:12 -04:00
assert_eq! (
at . plus ( "dir2/file" ). metadata (). unwrap (). gid (),
first_group . as_raw ()
);
assert_eq! (
at . plus ( "dir3/file" ). metadata (). unwrap (). gid (),
first_group . as_raw ()
);
2021-09-02 22:31:49 +02:00
ucmd . arg ( "-R" )
. args ( args )
. arg ( second_group . to_string ())
. arg ( "dir" )
. arg ( "dir3_ln" )
. succeeds ()
. no_stderr ();
assert_eq! (
at . plus ( "dir2/file" ). metadata (). unwrap (). gid (),
if traverse_second {
second_group . as_raw ()
} else {
first_group . as_raw ()
}
);
assert_eq! (
at . plus ( "dir3/file" ). metadata (). unwrap (). gid (),
if traverse_first {
second_group . as_raw ()
} else {
first_group . as_raw ()
}
);
}
}
2025-01-12 20:48:33 +01:00
#[test]
#[cfg(not(target_vendor = "apple" ))]
fn test_from_option () {
use std ::os ::unix ::fs ::MetadataExt ;
let scene = TestScenario ::new ( util_name! ());
let at = & scene . fixtures ;
let groups = nix ::unistd ::getgroups (). unwrap ();
// Skip test if we don't have at least two different groups to work with
if groups . len () < 2 {
return ;
}
let ( first_group , second_group ) = ( groups [ 0 ], groups [ 1 ]);
at . touch ( "test_file" );
scene
. ucmd ()
. arg ( first_group . to_string ())
. arg ( "test_file" )
. succeeds ();
// Test successful group change with --from
scene
. ucmd ()
. arg ( "--from" )
. arg ( first_group . to_string ())
. arg ( second_group . to_string ())
. arg ( "test_file" )
. succeeds ()
. no_stderr ();
// Verify the group was changed
let new_gid = at . plus ( "test_file" ). metadata (). unwrap (). gid ();
assert_eq! ( new_gid , second_group . as_raw ());
scene
. ucmd ()
. arg ( "--from" )
. arg ( first_group . to_string ())
. arg ( first_group . to_string ())
. arg ( "test_file" )
. succeeds ()
. no_stderr ();
let unchanged_gid = at . plus ( "test_file" ). metadata (). unwrap (). gid ();
assert_eq! ( unchanged_gid , second_group . as_raw ());
}
#[test]
#[cfg(not(any(target_os = "android" , target_os = "macos" )))]
fn test_from_with_invalid_group () {
let ( at , mut ucmd ) = at_and_ucmd! ();
at . touch ( "test_file" );
#[cfg(not(target_os = "android" ))]
let err_msg = "chgrp: invalid user: 'nonexistent_group' \n " ;
#[cfg(target_os = "android" )]
let err_msg = "chgrp: invalid user: 'staff' \n " ;
ucmd . arg ( "--from" )
. arg ( "nonexistent_group" )
. arg ( "staff" )
. arg ( "test_file" )
. fails ()
. stderr_is ( err_msg );
}
#[test]
#[cfg(not(target_vendor = "apple" ))]
fn test_verbosity_messages () {
let scene = TestScenario ::new ( util_name! ());
let at = & scene . fixtures ;
let groups = nix ::unistd ::getgroups (). unwrap ();
// Skip test if we don't have at least one group to work with
if groups . is_empty () {
return ;
}
at . touch ( "ref_file" );
at . touch ( "target_file" );
scene
. ucmd ()
. arg ( "-v" )
. arg ( "--reference=ref_file" )
. arg ( "target_file" )
. succeeds ()
. stderr_contains ( "group of 'target_file' retained as " );
}
#[test]
#[cfg(not(target_vendor = "apple" ))]
fn test_from_with_reference () {
use std ::os ::unix ::fs ::MetadataExt ;
let scene = TestScenario ::new ( util_name! ());
let at = & scene . fixtures ;
let groups = nix ::unistd ::getgroups (). unwrap ();
if groups . len () < 2 {
return ;
}
let ( first_group , second_group ) = ( groups [ 0 ], groups [ 1 ]);
at . touch ( "ref_file" );
at . touch ( "test_file" );
scene
. ucmd ()
. arg ( first_group . to_string ())
. arg ( "test_file" )
. succeeds ();
scene
. ucmd ()
. arg ( second_group . to_string ())
. arg ( "ref_file" )
. succeeds ();
// Test --from with --reference
scene
. ucmd ()
. arg ( "--from" )
. arg ( first_group . to_string ())
. arg ( "--reference=ref_file" )
. arg ( "test_file" )
. succeeds ()
. no_stderr ();
let new_gid = at . plus ( "test_file" ). metadata (). unwrap (). gid ();
let ref_gid = at . plus ( "ref_file" ). metadata (). unwrap (). gid ();
assert_eq! ( new_gid , ref_gid );
}
2025-01-12 21:17:04 +01:00
#[test]
#[cfg(not(target_vendor = "apple" ))]
fn test_numeric_group_formats () {
use std ::os ::unix ::fs ::MetadataExt ;
let scene = TestScenario ::new ( util_name! ());
let at = & scene . fixtures ;
let groups = nix ::unistd ::getgroups (). unwrap ();
if groups . len () < 2 {
return ;
}
let ( first_group , second_group ) = ( groups [ 0 ], groups [ 1 ]);
at . touch ( "test_file" );
scene
. ucmd ()
. arg ( first_group . to_string ())
. arg ( "test_file" )
. succeeds ();
// Test :gid format in --from
scene
. ucmd ()
. arg ( format! ( "--from=: {} " , first_group . as_raw ()))
. arg ( second_group . to_string ())
. arg ( "test_file" )
. succeeds ()
. no_stderr ();
let new_gid = at . plus ( "test_file" ). metadata (). unwrap (). gid ();
assert_eq! ( new_gid , second_group . as_raw ());
// Test :gid format in target group
scene
. ucmd ()
. arg ( format! ( "--from= {} " , second_group . as_raw ()))
. arg ( format! ( ": {} " , first_group . as_raw ()))
. arg ( "test_file" )
. succeeds ()
. no_stderr ();
let final_gid = at . plus ( "test_file" ). metadata (). unwrap (). gid ();
assert_eq! ( final_gid , first_group . as_raw ());
}
2025-08-08 15:26:19 +02:00
#[test]
#[cfg(target_os = "linux" )]
fn test_chgrp_non_utf8_paths () {
let ( at , mut ucmd ) = at_and_ucmd! ();
let filename = std ::ffi ::OsString ::from_vec ( vec! [ 0xFF , 0xFE ]);
std ::fs ::write ( at . plus ( & filename ), b "test content" ). unwrap ();
// Get current user's primary group
let current_gid = getegid ();
ucmd . arg ( current_gid . to_string ()). arg ( & filename ). succeeds ();
}
2025-09-23 09:28:15 +02:00
#[test]
fn test_chgrp_recursive_on_file () {
// Test for regression where `chgrp -R` on a regular file would fail
// with "Not a directory" error. This should succeed since there's nothing
// to recurse into, similar to GNU chgrp behavior.
// equivalent of tests/chgrp/recurse in GNU coreutils
use std ::os ::unix ::fs ::MetadataExt ;
let ( at , mut ucmd ) = at_and_ucmd! ();
at . touch ( "regular_file" );
let current_gid = getegid ();
ucmd . arg ( "-R" )
. arg ( current_gid . to_string ())
. arg ( "regular_file" )
. succeeds ()
. no_stderr ();
assert_eq! (
at . plus ( "regular_file" ). metadata (). unwrap (). gid (),
current_gid
);
}
2026-01-09 11:31:24 +01:00
#[test]
fn test_chgrp_exit_code_not_being_overwritten_by_last_file () {
use std ::os ::unix ::prelude ::PermissionsExt ;
let current_gid = getegid ();
let ( at , mut ucmd ) = at_and_ucmd! ();
at . mkdir ( "dir" );
at . mkdir ( "dir/a" );
at . mkdir ( "dir/b" );
at . touch ( "dir/b/file" );
at . touch ( "dir/a/file" );
std ::fs ::set_permissions ( at . plus ( "dir/a" ), PermissionsExt ::from_mode ( 0o0000 )). unwrap ();
// chgrp walks the dir alphabetically. Dir a does not have permissions so it fails, dir b does have
// permissions so it succeeds. We check that the overall command does fail although the
// last step succeeded.
ucmd . arg ( "-R" )
. arg ( current_gid . to_string ())
. arg ( "dir" )
. fails ();
}