2023-08-21 08:49:27 +00: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.
2023-12-15 21:49:35 +00:00
// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired tmpfs mdir COLORTERM mexe
2021-05-30 05:10:54 +00:00
2023-04-25 12:14:09 +00:00
#[ cfg(any(unix, feature = " feat_selinux " )) ]
2023-03-23 08:18:17 +00:00
use crate ::common ::util ::expected_result ;
2023-03-20 13:51:19 +00:00
use crate ::common ::util ::TestScenario ;
2022-01-30 16:45:01 +00:00
#[ cfg(all(unix, feature = " chmod " )) ]
2022-01-31 21:28:40 +00:00
use nix ::unistd ::{ close , dup } ;
2023-04-10 06:31:31 +00:00
use regex ::Regex ;
2021-05-05 21:03:25 +00:00
use std ::collections ::HashMap ;
2023-05-14 19:47:58 +00:00
#[ cfg(target_os = " linux " ) ]
use std ::ffi ::OsStr ;
#[ cfg(target_os = " linux " ) ]
use std ::os ::unix ::ffi ::OsStrExt ;
2022-01-30 16:45:01 +00:00
#[ cfg(all(unix, feature = " chmod " )) ]
2022-01-31 21:28:40 +00:00
use std ::os ::unix ::io ::IntoRawFd ;
2021-04-16 16:27:36 +00:00
use std ::path ::Path ;
2020-12-14 20:46:18 +00:00
#[ cfg(not(windows)) ]
2021-03-29 11:10:13 +00:00
use std ::path ::PathBuf ;
2022-01-31 21:28:40 +00:00
use std ::thread ::sleep ;
use std ::time ::Duration ;
2020-12-14 20:46:18 +00:00
2022-01-29 00:03:28 +00:00
const LONG_ARGS : & [ & str ] = & [
" -l " ,
" --long " ,
" --format=long " ,
" --for=long " ,
" --format=verbose " ,
" --for=verbose " ,
] ;
const ACROSS_ARGS : & [ & str ] = & [
" -x " ,
" --format=across " ,
" --format=horizontal " ,
" --for=across " ,
" --for=horizontal " ,
] ;
const COMMA_ARGS : & [ & str ] = & [ " -m " , " --format=commas " , " --for=commas " ] ;
const COLUMN_ARGS : & [ & str ] = & [ " -C " , " --format=columns " , " --for=columns " ] ;
2016-03-25 21:25:52 +00:00
#[ test ]
fn test_ls_ls ( ) {
2016-08-23 11:52:43 +00:00
new_ucmd! ( ) . succeeds ( ) ;
2016-05-22 07:46:54 +00:00
}
2016-12-25 05:32:12 +00:00
#[ test ]
2020-12-14 20:46:18 +00:00
fn test_ls_i ( ) {
2016-12-25 05:32:12 +00:00
new_ucmd! ( ) . arg ( " -i " ) . succeeds ( ) ;
new_ucmd! ( ) . arg ( " -il " ) . succeeds ( ) ;
}
ls: implement --color flag
GNU coreutils ls command implements the --color option as follow:
--color[=WHEN]
colorize the output; WHEN can be 'always' (default if omitted),
'auto', or 'never'
With --color=auto, ls emits color codes only when standard output is connected
to a terminal.
Also, add support for the following aliases:
- ‘always’, ‘yes’, ‘force’
- ‘never’, ‘no’, ‘none’
- ‘auto’, ‘tty’, ‘if-tty’
Signed-off-by: Gabriel Ganne <gabriel.ganne@gmail.com>
2019-06-20 07:55:01 +00:00
2022-01-05 13:50:37 +00:00
#[ test ]
fn test_ls_ordering ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " some-dir1 " ) ;
at . mkdir ( " some-dir2 " ) ;
at . mkdir ( " some-dir3 " ) ;
at . mkdir ( " some-dir4 " ) ;
at . mkdir ( " some-dir5 " ) ;
at . mkdir ( " some-dir6 " ) ;
scene
. ucmd ( )
. arg ( " -Rl " )
. succeeds ( )
. stdout_matches ( & Regex ::new ( " some-dir1: \\ ntotal 0 " ) . unwrap ( ) ) ;
}
2022-03-15 15:27:43 +00:00
#[ cfg(all(feature = " truncate " , feature = " dd " )) ]
2022-08-12 21:51:28 +00:00
#[ test ] // FIXME: fix this test for FreeBSD
2022-03-15 15:27:43 +00:00
fn test_ls_allocation_size ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " some-dir1 " ) ;
at . touch ( " some-dir1/empty-file " ) ;
#[ cfg(unix) ]
{
scene
. ccmd ( " truncate " )
. arg ( " -s " )
. arg ( " 4M " )
. arg ( " some-dir1/file-with-holes " )
. succeeds ( ) ;
// fill empty file with zeros
scene
. ccmd ( " dd " )
2022-09-12 14:06:31 +00:00
. arg ( " if=/dev/zero " )
. arg ( " of=some-dir1/zero-file " )
2022-03-15 15:27:43 +00:00
. arg ( " bs=1024 " )
. arg ( " count=4096 " )
. succeeds ( ) ;
scene
. ccmd ( " dd " )
2022-09-12 14:06:31 +00:00
. arg ( " if=/dev/zero " )
. arg ( " of=irregular-file " )
2022-03-15 15:27:43 +00:00
. arg ( " bs=1 " )
. arg ( " count=777 " )
. succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --block-size=512 " )
. arg ( " irregular-file " )
. succeeds ( )
. stdout_matches ( & Regex ::new ( " [^ ] 2 [^ ] " ) . unwrap ( ) ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. arg ( " -s1 " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_is ( " total 4096 \n 0 empty-file \n 0 file-with-holes \n 4096 zero-file \n " ) ;
scene
. ucmd ( )
. arg ( " -sl " )
. arg ( " some-dir1 " )
. succeeds ( )
// block size is 0 whereas size/len is 4194304
. stdout_contains ( " 4194304 " ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. arg ( " -s1 " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 4096 zero-file " ) ;
// Test alignment of different block sized files
let res = scene . ucmd ( ) . arg ( " -si1 " ) . arg ( " some-dir1 " ) . succeeds ( ) ;
let empty_file_len = String ::from_utf8 ( res . stdout ( ) . to_owned ( ) )
. ok ( )
. unwrap ( )
. lines ( )
. nth ( 1 )
. unwrap ( )
. strip_suffix ( " empty-file " )
. unwrap ( )
. len ( ) ;
let file_with_holes_len = String ::from_utf8 ( res . stdout ( ) . to_owned ( ) )
. ok ( )
. unwrap ( )
. lines ( )
. nth ( 2 )
. unwrap ( )
. strip_suffix ( " file-with-holes " )
. unwrap ( )
. len ( ) ;
assert_eq! ( empty_file_len , file_with_holes_len ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. env ( " LS_BLOCK_SIZE " , " 8K " )
. env ( " BLOCK_SIZE " , " 4K " )
. arg ( " -s1 " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 512 " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 512 zero-file " ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. env ( " BLOCK_SIZE " , " 4K " )
. arg ( " -s1 " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 1024 " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 1024 zero-file " ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. env ( " BLOCK_SIZE " , " 4K " )
. arg ( " -s1 " )
. arg ( " --si " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 4.2M " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 4.2M zero-file " ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. env ( " BLOCK_SIZE " , " 4096 " )
. arg ( " -s1 " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 1024 " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 1024 zero-file " ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. env ( " POSIXLY_CORRECT " , " true " )
. arg ( " -s1 " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 8192 " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 8192 zero-file " ) ;
// -k should make 'ls' ignore the env var
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. env ( " BLOCK_SIZE " , " 4K " )
. arg ( " -s1k " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 4096 " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 4096 zero-file " ) ;
// but manually specified blocksize overrides -k
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. arg ( " -s1k " )
. arg ( " --block-size=4K " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 1024 " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 1024 zero-file " ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. arg ( " -s1 " )
. arg ( " --block-size=4K " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 1024 " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 1024 zero-file " ) ;
// si option should always trump the human-readable option
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. arg ( " -s1h " )
. arg ( " --si " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 4.2M " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 4.2M zero-file " ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. arg ( " -s1 " )
. arg ( " --block-size=human-readable " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 4.0M " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 4.0M zero-file " ) ;
2022-08-12 21:51:28 +00:00
#[ cfg(not(target_os = " freebsd " )) ]
2022-03-15 15:27:43 +00:00
scene
. ucmd ( )
. arg ( " -s1 " )
. arg ( " --block-size=si " )
. arg ( " some-dir1 " )
. succeeds ( )
. stdout_contains ( " total 4.2M " )
. stdout_contains ( " 0 empty-file " )
. stdout_contains ( " 0 file-with-holes " )
. stdout_contains ( " 4.2M zero-file " ) ;
}
}
2022-01-14 23:39:56 +00:00
#[ test ]
fn test_ls_devices ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " some-dir1 " ) ;
// Regex tests correct device ID and correct (no pad) spacing for a single file
#[ cfg(any(target_os = " macos " , target_os = " ios " )) ]
{
scene
. ucmd ( )
. arg ( " -al " )
. arg ( " /dev/null " )
. succeeds ( )
. stdout_matches ( & Regex ::new ( " [^ ] 3, 2 [^ ] " ) . unwrap ( ) ) ;
}
2022-02-09 18:08:28 +00:00
#[ cfg(any(target_os = " linux " , target_os = " android " )) ]
2022-01-14 23:39:56 +00:00
{
scene
. ucmd ( )
. arg ( " -al " )
. arg ( " /dev/null " )
. succeeds ( )
. stdout_matches ( & Regex ::new ( " [^ ] 1, 3 [^ ] " ) . unwrap ( ) ) ;
}
2022-03-15 15:27:43 +00:00
// Tests display alignment against a file (stdout is a link to a tty)
2022-01-14 23:39:56 +00:00
#[ cfg(unix) ]
{
2022-02-09 18:08:28 +00:00
#[ cfg(not(target_os = " android " )) ]
let stdout = " /dev/stdout " ;
#[ cfg(target_os = " android " ) ]
let stdout = " /proc/self/fd/1 " ;
2022-01-14 23:39:56 +00:00
let res = scene
. ucmd ( )
. arg ( " -alL " )
. arg ( " /dev/null " )
2022-02-09 18:08:28 +00:00
. arg ( stdout )
2022-01-14 23:39:56 +00:00
. succeeds ( ) ;
let null_len = String ::from_utf8 ( res . stdout ( ) . to_owned ( ) )
. ok ( )
. unwrap ( )
. lines ( )
. next ( )
. unwrap ( )
. strip_suffix ( " /dev/null " )
. unwrap ( )
. len ( ) ;
let stdout_len = String ::from_utf8 ( res . stdout ( ) . to_owned ( ) )
. ok ( )
. unwrap ( )
. lines ( )
. nth ( 1 )
. unwrap ( )
2022-02-09 18:08:28 +00:00
. strip_suffix ( stdout )
2022-01-14 23:39:56 +00:00
. unwrap ( )
. len ( ) ;
assert_eq! ( stdout_len , null_len ) ;
}
}
2022-01-31 21:28:40 +00:00
#[ cfg(feature = " chmod " ) ]
2022-01-05 13:50:37 +00:00
#[ test ]
fn test_ls_io_errors ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " some-dir1 " ) ;
at . mkdir ( " some-dir2 " ) ;
at . symlink_file ( " does_not_exist " , " some-dir2/dangle " ) ;
at . mkdir ( " some-dir3 " ) ;
at . mkdir ( " some-dir3/some-dir4 " ) ;
2022-01-16 04:39:07 +00:00
at . mkdir ( " some-dir4 " ) ;
2022-01-05 13:50:37 +00:00
scene . ccmd ( " chmod " ) . arg ( " 000 " ) . arg ( " some-dir1 " ) . succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -1 " )
. arg ( " some-dir1 " )
. fails ( )
2022-07-10 20:12:59 +00:00
. code_is ( 2 )
2022-01-05 13:50:37 +00:00
. stderr_contains ( " cannot open directory " )
. stderr_contains ( " Permission denied " ) ;
scene
. ucmd ( )
. arg ( " -Li " )
. arg ( " some-dir2 " )
. fails ( )
2022-07-10 20:12:59 +00:00
. code_is ( 1 )
2022-01-05 13:50:37 +00:00
. stderr_contains ( " cannot access " )
. stderr_contains ( " No such file or directory " )
. stdout_contains ( if cfg! ( windows ) { " dangle " } else { " ? dangle " } ) ;
scene
. ccmd ( " chmod " )
. arg ( " 000 " )
. arg ( " some-dir3/some-dir4 " )
. succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -laR " )
. arg ( " some-dir3 " )
. fails ( )
2022-07-10 20:12:59 +00:00
. code_is ( 1 )
2022-01-05 13:50:37 +00:00
. stderr_contains ( " some-dir4 " )
. stderr_contains ( " cannot open directory " )
. stderr_contains ( " Permission denied " )
. stdout_contains ( " some-dir4 " ) ;
2022-01-08 18:10:52 +00:00
2022-01-16 16:20:50 +00:00
// don't double print on dangling link metadata errors
2022-01-08 18:10:52 +00:00
scene
. ucmd ( )
. arg ( " -iRL " )
. arg ( " some-dir2 " )
. fails ( )
. stderr_does_not_contain (
" ls: cannot access 'some-dir2/dangle': No such file or directory \n ls: cannot access 'some-dir2/dangle': No such file or directory "
) ;
2022-01-16 04:39:07 +00:00
#[ cfg(unix) ]
{
at . touch ( " some-dir4/bad-fd.txt " ) ;
2022-01-31 21:28:40 +00:00
let fd1 = at . open ( " some-dir4/bad-fd.txt " ) . into_raw_fd ( ) ;
let fd2 = dup ( dbg! ( fd1 ) ) . unwrap ( ) ;
close ( fd1 ) . unwrap ( ) ;
2022-01-17 19:14:43 +00:00
2022-01-31 21:28:40 +00:00
// on the mac and in certain Linux containers bad fds are typed as dirs,
// however sometimes bad fds are typed as links and directory entry on links won't fail
2023-01-27 09:29:45 +00:00
if PathBuf ::from ( format! ( " /dev/fd/ {fd2} " ) ) . is_dir ( ) {
2022-01-17 19:14:43 +00:00
scene
. ucmd ( )
2022-01-31 21:28:40 +00:00
. arg ( " -alR " )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " /dev/fd/ {fd2} " ) )
2022-01-31 21:28:40 +00:00
. fails ( )
. stderr_contains ( format! (
2023-01-27 09:29:45 +00:00
" cannot open directory '/dev/fd/{fd2}': Bad file descriptor "
2022-01-31 21:28:40 +00:00
) )
2023-01-27 09:29:45 +00:00
. stdout_does_not_contain ( format! ( " {fd2} : \n " ) ) ;
2022-01-31 21:28:40 +00:00
scene
. ucmd ( )
. arg ( " -RiL " )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " /dev/fd/ {fd2} " ) )
2022-01-31 21:28:40 +00:00
. fails ( )
2023-01-27 09:29:45 +00:00
. stderr_contains ( format! ( " cannot open directory '/dev/fd/ {fd2} ': Bad file descriptor " ) )
2022-01-31 21:28:40 +00:00
// don't double print bad fd errors
2023-01-27 09:29:45 +00:00
. stderr_does_not_contain ( format! ( " ls: cannot open directory '/dev/fd/ {fd2} ': Bad file descriptor \n ls: cannot open directory '/dev/fd/ {fd2} ': Bad file descriptor " ) ) ;
2022-01-31 21:28:40 +00:00
} else {
scene
. ucmd ( )
. arg ( " -alR " )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " /dev/fd/ {fd2} " ) )
2022-01-31 21:28:40 +00:00
. succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -RiL " )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " /dev/fd/ {fd2} " ) )
2022-01-17 19:14:43 +00:00
. succeeds ( ) ;
2022-01-16 16:20:50 +00:00
}
2022-01-31 21:28:40 +00:00
scene
. ucmd ( )
. arg ( " -alL " )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " /dev/fd/ {fd2} " ) )
2022-01-31 21:28:40 +00:00
. succeeds ( ) ;
2022-01-16 04:39:07 +00:00
let _ = close ( fd2 ) ;
}
2022-01-05 13:50:37 +00:00
}
2022-01-06 20:58:56 +00:00
#[ test ]
fn test_ls_only_dirs_formatting ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " some-dir1 " ) ;
at . mkdir ( " some-dir2 " ) ;
at . mkdir ( " some-dir3 " ) ;
2022-01-06 21:41:53 +00:00
#[ cfg(unix) ]
{
scene . ucmd ( ) . arg ( " -1 " ) . arg ( " -R " ) . succeeds ( ) . stdout_only (
" .: \n some-dir1 \n some-dir2 \n some-dir3 \n \n ./some-dir1: \n \n ./some-dir2: \n \n ./some-dir3: \n " ,
) ;
}
#[ cfg(windows) ]
{
scene . ucmd ( ) . arg ( " -1 " ) . arg ( " -R " ) . succeeds ( ) . stdout_only (
" .: \n some-dir1 \n some-dir2 \n some-dir3 \n \n . \\ some-dir1: \n \n . \\ some-dir2: \n \n . \\ some-dir3: \n " ,
) ;
}
2022-01-06 20:58:56 +00:00
}
2020-12-14 20:46:18 +00:00
#[ test ]
2021-12-22 17:31:45 +00:00
fn test_ls_walk_glob ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " .test-1 " ) ;
at . mkdir ( " some-dir " ) ;
at . touch (
Path ::new ( " some-dir " )
. join ( " test-2~ " )
. as_os_str ( )
. to_str ( )
. unwrap ( ) ,
) ;
#[ allow(clippy::trivial_regex) ]
let re_pwd = Regex ::new ( r "^\.\n" ) . unwrap ( ) ;
scene
. ucmd ( )
. arg ( " -1 " )
. arg ( " --ignore-backups " )
. arg ( " some-dir " )
. succeeds ( )
. stdout_does_not_contain ( " test-2~ " )
. stdout_does_not_contain ( " .. " )
. stdout_does_not_match ( & re_pwd ) ;
}
#[ test ]
#[ cfg(unix) ]
2020-12-14 20:46:18 +00:00
fn test_ls_a ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " .test-1 " ) ;
2021-05-02 08:03:01 +00:00
at . mkdir ( " some-dir " ) ;
at . touch (
Path ::new ( " some-dir " )
. join ( " .test-2 " )
. as_os_str ( )
. to_str ( )
. unwrap ( ) ,
) ;
2020-12-14 20:46:18 +00:00
2021-05-29 12:32:35 +00:00
#[ allow(clippy::trivial_regex) ]
2021-05-02 08:03:01 +00:00
let re_pwd = Regex ::new ( r "^\.\n" ) . unwrap ( ) ;
// Using the present working directory
scene
. ucmd ( )
2021-05-02 12:26:23 +00:00
. arg ( " -1 " )
2021-05-02 08:03:01 +00:00
. succeeds ( )
. stdout_does_not_contain ( " .test-1 " )
. stdout_does_not_contain ( " .. " )
. stdout_does_not_match ( & re_pwd ) ;
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -a " )
2021-05-02 12:26:23 +00:00
. arg ( " -1 " )
2021-04-07 09:48:01 +00:00
. succeeds ( )
2022-10-12 20:04:21 +00:00
. stdout_contains ( " .test-1 " )
. stdout_contains ( " .. " )
2021-05-02 08:03:01 +00:00
. stdout_matches ( & re_pwd ) ;
scene
. ucmd ( )
. arg ( " -A " )
2021-05-02 12:26:23 +00:00
. arg ( " -1 " )
2021-05-02 08:03:01 +00:00
. succeeds ( )
. stdout_contains ( " .test-1 " )
. stdout_does_not_contain ( " .. " )
. stdout_does_not_match ( & re_pwd ) ;
2021-04-07 09:48:01 +00:00
2021-05-02 08:03:01 +00:00
// Using a subdirectory
scene
. ucmd ( )
2021-05-02 12:26:23 +00:00
. arg ( " -1 " )
2021-05-02 08:03:01 +00:00
. arg ( " some-dir " )
. succeeds ( )
. stdout_does_not_contain ( " .test-2 " )
. stdout_does_not_contain ( " .. " )
. stdout_does_not_match ( & re_pwd ) ;
scene
. ucmd ( )
. arg ( " -a " )
2021-05-02 12:26:23 +00:00
. arg ( " -1 " )
2021-05-02 08:03:01 +00:00
. arg ( " some-dir " )
. succeeds ( )
2022-10-12 20:04:21 +00:00
. stdout_contains ( " .test-2 " )
. stdout_contains ( " .. " )
2021-05-02 08:03:01 +00:00
. no_stderr ( )
. stdout_matches ( & re_pwd ) ;
2021-04-07 09:48:01 +00:00
2021-05-02 08:03:01 +00:00
scene
. ucmd ( )
. arg ( " -A " )
2021-05-02 12:26:23 +00:00
. arg ( " -1 " )
2021-05-02 08:03:01 +00:00
. arg ( " some-dir " )
. succeeds ( )
. stdout_contains ( " .test-2 " )
. stdout_does_not_contain ( " .. " )
. stdout_does_not_match ( & re_pwd ) ;
2020-12-14 20:46:18 +00:00
}
2021-03-22 17:24:23 +00:00
#[ test ]
fn test_ls_width ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-width-1 " ) ) ;
at . touch ( at . plus_as_string ( " test-width-2 " ) ) ;
at . touch ( at . plus_as_string ( " test-width-3 " ) ) ;
at . touch ( at . plus_as_string ( " test-width-4 " ) ) ;
2021-03-22 17:24:23 +00:00
2022-04-02 08:47:37 +00:00
for option in [
2022-01-29 00:03:28 +00:00
" -w 100 " ,
" -w=100 " ,
" --width=100 " ,
" --width 100 " ,
" --wid=100 " ,
] {
2021-04-07 09:48:01 +00:00
scene
2021-03-22 17:24:23 +00:00
. ucmd ( )
2021-05-29 12:32:35 +00:00
. args ( & option . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
2021-08-11 20:03:41 +00:00
. arg ( " -C " )
2021-04-07 09:48:01 +00:00
. succeeds ( )
. stdout_only ( " test-width-1 test-width-2 test-width-3 test-width-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
2022-04-02 08:47:37 +00:00
for option in [ " -w 50 " , " -w=50 " , " --width=50 " , " --width 50 " , " --wid=50 " ] {
2021-04-07 09:48:01 +00:00
scene
2021-03-22 17:24:23 +00:00
. ucmd ( )
2021-05-29 12:32:35 +00:00
. args ( & option . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
2021-08-11 20:03:41 +00:00
. arg ( " -C " )
2021-04-07 09:48:01 +00:00
. succeeds ( )
. stdout_only ( " test-width-1 test-width-3 \n test-width-2 test-width-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
2022-04-02 08:47:37 +00:00
for option in [ " -w 25 " , " -w=25 " , " --width=25 " , " --width 25 " , " --wid=25 " ] {
2021-04-07 09:48:01 +00:00
scene
2021-03-22 17:24:23 +00:00
. ucmd ( )
2021-05-29 12:32:35 +00:00
. args ( & option . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
2021-08-11 20:03:41 +00:00
. arg ( " -C " )
2021-04-07 09:48:01 +00:00
. succeeds ( )
. stdout_only ( " test-width-1 \n test-width-2 \n test-width-3 \n test-width-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
2021-04-22 07:19:17 +00:00
2022-04-02 08:47:37 +00:00
for option in [ " -w 0 " , " -w=0 " , " --width=0 " , " --width 0 " , " --wid=0 " ] {
2021-08-11 13:53:39 +00:00
scene
. ucmd ( )
. args ( & option . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
2021-08-11 20:03:41 +00:00
. arg ( " -C " )
2021-08-11 13:53:39 +00:00
. succeeds ( )
. stdout_only ( " test-width-1 test-width-2 test-width-3 test-width-4 \n " ) ;
}
2022-06-12 10:55:33 +00:00
for option in [
" -w 062 " ,
" -w=062 " ,
" --width=062 " ,
" --width 062 " ,
" --wid=062 " ,
] {
2022-06-11 20:43:46 +00:00
scene
. ucmd ( )
. args ( & option . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
. arg ( " -C " )
2022-06-12 10:55:33 +00:00
. succeeds ( )
. stdout_only ( " test-width-1 test-width-3 \n test-width-2 test-width-4 \n " ) ;
2022-06-11 20:43:46 +00:00
}
2023-07-12 17:54:26 +00:00
for option in [
" -w 100000000000000 " ,
" -w=100000000000000 " ,
" --width=100000000000000 " ,
" --width 100000000000000 " ,
" -w 07777777777777777777 " ,
" -w=07777777777777777777 " ,
" --width=07777777777777777777 " ,
" --width 07777777777777777777 " ,
] {
scene
. ucmd ( )
. args ( & option . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
. arg ( " -C " )
. succeeds ( )
. stdout_only ( " test-width-1 test-width-2 test-width-3 test-width-4 \n " ) ;
}
2021-04-21 10:03:48 +00:00
scene
. ucmd ( )
. arg ( " -w=bad " )
2021-08-11 20:03:41 +00:00
. arg ( " -C " )
2021-04-21 10:03:48 +00:00
. fails ( )
. stderr_contains ( " invalid line width " ) ;
2021-04-22 12:23:35 +00:00
2022-04-02 08:47:37 +00:00
for option in [ " -w 1a " , " -w=1a " , " --width=1a " , " --width 1a " , " --wid 1a " ] {
2021-04-22 07:19:17 +00:00
scene
. ucmd ( )
2021-05-29 12:32:35 +00:00
. args ( & option . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
2021-08-11 20:03:41 +00:00
. arg ( " -C " )
2021-04-22 07:19:17 +00:00
. fails ( )
2023-01-05 20:09:15 +00:00
. stderr_only ( " ls: invalid line width: '1a' \n " ) ;
2021-04-22 07:19:17 +00:00
}
2021-03-22 17:24:23 +00:00
}
2021-03-15 12:46:21 +00:00
#[ test ]
fn test_ls_columns ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-columns-1 " ) ) ;
at . touch ( at . plus_as_string ( " test-columns-2 " ) ) ;
at . touch ( at . plus_as_string ( " test-columns-3 " ) ) ;
at . touch ( at . plus_as_string ( " test-columns-4 " ) ) ;
2021-03-15 12:46:21 +00:00
// Columns is the default
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . succeeds ( ) ;
2021-03-15 12:46:21 +00:00
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-columns-1 \n test-columns-2 \n test-columns-3 \n test-columns-4 \n " ) ;
2021-03-15 12:46:21 +00:00
2022-01-29 00:03:28 +00:00
for option in COLUMN_ARGS {
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( option ) . succeeds ( ) ;
result . stdout_only ( " test-columns-1 test-columns-2 test-columns-3 test-columns-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
2022-01-29 00:03:28 +00:00
for option in COLUMN_ARGS {
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -w=40 " )
. arg ( option )
. succeeds ( )
. stdout_only ( " test-columns-1 test-columns-3 \n test-columns-2 test-columns-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
2021-08-11 12:32:46 +00:00
2021-08-11 13:53:39 +00:00
// On windows we are always able to get the terminal size, so we can't simulate falling back to the
// environment variable.
#[ cfg(not(windows)) ]
{
2022-01-29 00:03:28 +00:00
for option in COLUMN_ARGS {
2021-08-11 13:53:39 +00:00
scene
. ucmd ( )
. env ( " COLUMNS " , " 40 " )
. arg ( option )
. succeeds ( )
. stdout_only ( " test-columns-1 test-columns-3 \n test-columns-2 test-columns-4 \n " ) ;
}
2021-08-11 12:32:46 +00:00
scene
. ucmd ( )
2021-08-11 13:53:39 +00:00
. env ( " COLUMNS " , " garbage " )
2021-08-11 20:03:41 +00:00
. arg ( " -C " )
2021-08-11 12:32:46 +00:00
. succeeds ( )
2021-08-11 13:53:39 +00:00
. stdout_is ( " test-columns-1 test-columns-2 test-columns-3 test-columns-4 \n " )
2023-01-05 20:09:15 +00:00
. stderr_is ( " ls: ignoring invalid width in environment variable COLUMNS: 'garbage' \n " ) ;
2021-08-11 12:32:46 +00:00
}
scene
. ucmd ( )
2021-08-11 20:03:41 +00:00
. arg ( " -Cw0 " )
2021-08-11 13:53:39 +00:00
. succeeds ( )
. stdout_only ( " test-columns-1 test-columns-2 test-columns-3 test-columns-4 \n " ) ;
scene
. ucmd ( )
. arg ( " -mw0 " )
2021-08-11 12:32:46 +00:00
. succeeds ( )
2021-08-11 13:53:39 +00:00
. stdout_only ( " test-columns-1, test-columns-2, test-columns-3, test-columns-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
#[ test ]
fn test_ls_across ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-across-1 " ) ) ;
at . touch ( at . plus_as_string ( " test-across-2 " ) ) ;
at . touch ( at . plus_as_string ( " test-across-3 " ) ) ;
at . touch ( at . plus_as_string ( " test-across-4 " ) ) ;
2021-03-22 17:24:23 +00:00
2022-01-29 00:03:28 +00:00
for option in ACROSS_ARGS {
2021-03-22 17:24:23 +00:00
let result = scene . ucmd ( ) . arg ( option ) . succeeds ( ) ;
// Because the test terminal has width 0, this is the same output as
// the columns option.
2021-08-11 12:32:46 +00:00
result . stdout_only ( " test-across-1 test-across-2 test-across-3 test-across-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
2022-01-29 00:03:28 +00:00
for option in ACROSS_ARGS {
2021-03-22 17:24:23 +00:00
// Because the test terminal has width 0, this is the same output as
// the columns option.
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -w=30 " )
. arg ( option )
. succeeds ( )
. stdout_only ( " test-across-1 test-across-2 \n test-across-3 test-across-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
}
#[ test ]
fn test_ls_commas ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-commas-1 " ) ) ;
at . touch ( at . plus_as_string ( " test-commas-2 " ) ) ;
at . touch ( at . plus_as_string ( " test-commas-3 " ) ) ;
at . touch ( at . plus_as_string ( " test-commas-4 " ) ) ;
2021-03-22 17:24:23 +00:00
2022-01-29 00:03:28 +00:00
for option in COMMA_ARGS {
2021-03-22 17:24:23 +00:00
let result = scene . ucmd ( ) . arg ( option ) . succeeds ( ) ;
2021-08-11 12:32:46 +00:00
result . stdout_only ( " test-commas-1, test-commas-2, test-commas-3, test-commas-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
2022-01-29 00:03:28 +00:00
for option in COMMA_ARGS {
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -w=30 " )
. arg ( option )
. succeeds ( )
. stdout_only ( " test-commas-1, test-commas-2, \n test-commas-3, test-commas-4 \n " ) ;
2021-03-22 17:24:23 +00:00
}
2022-01-29 00:03:28 +00:00
for option in COMMA_ARGS {
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -w=45 " )
. arg ( option )
. succeeds ( )
. stdout_only ( " test-commas-1, test-commas-2, test-commas-3, \n test-commas-4 \n " ) ;
2021-03-15 12:46:21 +00:00
}
}
2022-07-31 17:02:50 +00:00
#[ test ]
fn test_ls_zero ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " 0-test-zero " ) ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " 2-test-zero " ) ) ;
at . touch ( at . plus_as_string ( " 3-test-zero " ) ) ;
2022-07-31 17:02:50 +00:00
let ignored_opts = [
" --quoting-style=c " ,
" --color=always " ,
" -m " ,
" --hide-control-chars " ,
] ;
scene
. ucmd ( )
. arg ( " --zero " )
. succeeds ( )
. stdout_only ( " 0-test-zero \x00 2-test-zero \x00 3-test-zero \x00 " ) ;
for opt in ignored_opts {
scene
. ucmd ( )
. args ( & [ opt , " --zero " ] )
. succeeds ( )
. stdout_only ( " 0-test-zero \x00 2-test-zero \x00 3-test-zero \x00 " ) ;
}
scene
. ucmd ( )
. args ( & [ " --zero " , " --quoting-style=c " ] )
. succeeds ( )
. stdout_only ( " \" 0-test-zero \" \x00 \" 2-test-zero \" \x00 \" 3-test-zero \" \x00 " ) ;
2023-11-30 10:04:14 +00:00
let result = scene . ucmd ( ) . args ( & [ " --zero " , " --color=always " ] ) . succeeds ( ) ;
assert_eq! (
result . stdout_str ( ) ,
" \u{1b} [0m \u{1b} [01;34m0-test-zero \x1b [0m \x00 2-test-zero \x00 3-test-zero \x00 "
) ;
2022-07-31 17:02:50 +00:00
scene
. ucmd ( )
. args ( & [ " --zero " , " -m " ] )
. succeeds ( )
. stdout_only ( " 0-test-zero, 2-test-zero, 3-test-zero \x00 " ) ;
scene
. ucmd ( )
. args ( & [ " --zero " , " --hide-control-chars " ] )
. succeeds ( )
. stdout_only ( " 0-test-zero \x00 2-test-zero \x00 3-test-zero \x00 " ) ;
scene
. ucmd ( )
. args ( & [ " --zero " , " --quoting-style=c " , " --zero " ] )
. succeeds ( )
. stdout_only ( " 0-test-zero \x00 2-test-zero \x00 3-test-zero \x00 " ) ;
#[ cfg(unix) ]
{
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " 1 \n test-zero " ) ) ;
2022-07-31 17:02:50 +00:00
let ignored_opts = [
" --quoting-style=c " ,
" --color=always " ,
" -m " ,
" --hide-control-chars " ,
] ;
scene
. ucmd ( )
. arg ( " --zero " )
. succeeds ( )
. stdout_only ( " 0-test-zero \x00 1 \n test-zero \x00 2-test-zero \x00 3-test-zero \x00 " ) ;
for opt in ignored_opts {
scene
. ucmd ( )
. args ( & [ opt , " --zero " ] )
. succeeds ( )
. stdout_only ( " 0-test-zero \x00 1 \n test-zero \x00 2-test-zero \x00 3-test-zero \x00 " ) ;
}
scene
. ucmd ( )
. args ( & [ " --zero " , " --quoting-style=c " ] )
. succeeds ( )
. stdout_only (
" \" 0-test-zero \" \x00 \" 1 \\ ntest-zero \" \x00 \" 2-test-zero \" \x00 \" 3-test-zero \" \x00 " ,
) ;
2023-11-30 10:04:14 +00:00
let result = scene . ucmd ( ) . args ( & [ " --zero " , " --color=always " ] ) . succeeds ( ) ;
assert_eq! ( result . stdout_str ( ) ,
" \u{1b} [0m \u{1b} [01;34m0-test-zero \x1b [0m \x00 1 \n test-zero \x00 2-test-zero \x00 3-test-zero \x00 " ,
2022-07-31 17:02:50 +00:00
) ;
scene
. ucmd ( )
. args ( & [ " --zero " , " -m " ] )
. succeeds ( )
. stdout_only ( " 0-test-zero, 1 \n test-zero, 2-test-zero, 3-test-zero \x00 " ) ;
scene
. ucmd ( )
. args ( & [ " --zero " , " --hide-control-chars " ] )
. succeeds ( )
. stdout_only ( " 0-test-zero \x00 1?test-zero \x00 2-test-zero \x00 3-test-zero \x00 " ) ;
}
scene
. ucmd ( )
. args ( & [ " -l " , " --zero " ] )
. succeeds ( )
. stdout_contains ( " total " ) ;
}
2022-06-13 09:29:13 +00:00
#[ test ]
fn test_ls_commas_trailing ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-commas-trailing-2 " ) ) ;
2022-06-13 09:29:13 +00:00
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-commas-trailing-1 " ) ) ;
2022-06-13 09:29:13 +00:00
at . append (
" test-commas-trailing-1 " ,
& ( 0 .. 2000 )
. map ( | x | x . to_string ( ) )
. collect ::< Vec < _ > > ( )
. join ( " \n " ) ,
) ;
scene
. ucmd ( )
. arg ( " -sm " )
. arg ( " ./test-commas-trailing-1 " )
. arg ( " ./test-commas-trailing-2 " )
. succeeds ( )
2023-02-16 14:33:33 +00:00
. stdout_matches ( & Regex ::new ( r "\S\n$" ) . unwrap ( ) ) ;
2022-06-13 09:29:13 +00:00
}
2020-12-14 20:46:18 +00:00
#[ test ]
fn test_ls_long ( ) {
2021-03-15 12:46:21 +00:00
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-long " ) ) ;
2020-12-14 20:46:18 +00:00
2022-01-29 00:03:28 +00:00
for arg in LONG_ARGS {
2021-03-15 12:46:21 +00:00
let result = scene . ucmd ( ) . arg ( arg ) . arg ( " test-long " ) . succeeds ( ) ;
#[ cfg(not(windows)) ]
2022-01-30 16:45:01 +00:00
result . stdout_matches ( & Regex ::new ( r "[-bcCdDlMnpPsStTx?]([r-][w-][xt-]){3}.*" ) . unwrap ( ) ) ;
2021-03-15 12:46:21 +00:00
#[ cfg(windows) ]
2022-10-09 10:03:44 +00:00
result . stdout_matches ( & Regex ::new ( r "[-dl](r[w-]x){3}.*" ) . unwrap ( ) ) ;
2021-03-15 12:46:21 +00:00
}
2021-05-05 21:03:25 +00:00
}
2021-09-05 11:25:56 +00:00
#[ cfg(not(windows)) ]
2021-08-30 21:09:16 +00:00
#[ test ]
fn test_ls_long_format ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-11-16 15:02:38 +00:00
at . mkdir ( at . plus_as_string ( " test-long-dir " ) ) ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-long-dir/test-long-file " ) ) ;
2023-11-16 15:02:38 +00:00
at . mkdir ( at . plus_as_string ( " test-long-dir/test-long-dir " ) ) ;
2021-08-30 21:09:16 +00:00
2022-01-29 00:03:28 +00:00
for arg in LONG_ARGS {
2021-08-30 21:09:16 +00:00
// Assuming sane username do not have spaces within them.
// A line of the output should be:
// One of the characters -bcCdDlMnpPsStTx?
// rwx, with - for missing permissions, thrice.
2021-09-10 19:35:00 +00:00
// Zero or one "." for indicating a file with security context
2021-08-30 21:09:16 +00:00
// A number, preceded by column whitespace, and followed by a single space.
// A username, currently [^ ], followed by column whitespace, twice (or thrice for Hurd).
// A number, followed by a single space.
// A month, followed by a single space.
// A day, preceded by column whitespace, and followed by a single space.
// Either a year or a time, currently [0-9:]+, preceded by column whitespace,
// and followed by a single space.
// Whatever comes after is irrelevant to this specific test.
2021-09-05 11:25:56 +00:00
scene . ucmd ( ) . arg ( arg ) . arg ( " test-long-dir " ) . succeeds ( ) . stdout_matches ( & Regex ::new (
2021-09-10 19:35:00 +00:00
r "\n[-bcCdDlMnpPsStTx?]([r-][w-][xt-]){3}\.? +\d+ [^ ]+ +[^ ]+( +[^ ]+)? +\d+ [A-Z][a-z]{2} {0,2}\d{0,2} {0,2}[0-9:]+ "
2021-08-30 21:09:16 +00:00
) . unwrap ( ) ) ;
}
// This checks for the line with the .. entry. The uname and group should be digits.
2021-09-05 11:25:56 +00:00
scene . ucmd ( ) . arg ( " -lan " ) . arg ( " test-long-dir " ) . succeeds ( ) . stdout_matches ( & Regex ::new (
2021-09-10 19:35:00 +00:00
r "\nd([r-][w-][xt-]){3}\.? +\d+ \d+ +\d+( +\d+)? +\d+ [A-Z][a-z]{2} {0,2}\d{0,2} {0,2}[0-9:]+ \.\."
2021-08-30 21:09:16 +00:00
) . unwrap ( ) ) ;
2021-09-05 11:25:56 +00:00
}
2021-08-30 21:09:16 +00:00
2023-11-30 15:13:54 +00:00
#[ test ]
fn test_ls_long_padding_of_size_column_with_multiple_files ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkdir ( " dir " ) ;
at . touch ( " dir/a " ) ;
at . touch ( " dir/b " ) ;
ucmd . arg ( " -l " )
. arg ( " dir " )
. succeeds ( )
. stdout_contains ( " 0 " )
. stdout_does_not_contain ( " 0 " ) ;
}
2021-09-05 11:25:56 +00:00
/// This test tests `ls -laR --color`.
/// This test is mainly about coloring, but, the recursion, symlink `->` processing,
/// and `.` and `..` being present in `-a` all need to work for the test to pass.
/// This test does not really test anything provided by `-l` but the file names and symlinks.
2021-09-10 19:35:00 +00:00
#[ cfg(all(feature = " ln " , feature = " mkdir " , feature = " touch " )) ]
2021-09-05 11:25:56 +00:00
#[ test ]
2021-09-10 16:36:51 +00:00
#[ cfg(all(feature = " ln " , feature = " mkdir " , feature = " touch " )) ]
2021-09-05 11:25:56 +00:00
fn test_ls_long_symlink_color ( ) {
// If you break this test after breaking mkdir, touch, or ln, do not be alarmed!
// This test is made for ls, but it attempts to run those utils in the process.
// Having Some([2, 0]) in a color basically means that "it has the same color as whatever
// is in the 2nd expected output, the 0th color", where the 0th color is the name color, and
// the 1st color is the target color, in a fixed-size array of size 2.
// Basically these are references to be used for indexing the `colors` vector defined below.
type ColorReference = Option < [ usize ; 2 ] > ;
// The string between \x1b[ and m
type Color = String ;
// The string between the color start and the color end is the file name itself.
type Name = String ;
let scene = TestScenario ::new ( util_name! ( ) ) ;
// .
// ├── dir1
// │ ├── file1
// │ ├── dir2
// │ │ └── dir3
// │ ├── ln-dir-invalid -> dir1/dir2
// │ ├── ln-up2 -> ../..
// │ └── ln-root -> /
// ├── ln-file1 -> dir1/file1
// ├── ln-file-invalid -> dir1/invalid-target
// └── ln-dir3 -> ./dir1/dir2/dir3
prepare_folder_structure ( & scene ) ;
// We memoize the colors so we can refer to them later.
// Each entry will be the colors of the link name and link target of a specific output.
let mut colors : Vec < [ Color ; 2 ] > = vec! [ ] ;
// The contents of each tuple are the expected colors and names for the link and target.
// We will loop over the ls output and compare to those.
// None values mean that we do not know what color to expect yet, as LS_COLOR might
// be set differently, and as different implementations of ls may use different codes,
// for example, our ls uses `[1;36m` while the GNU ls uses `[01;36m`.
//
// These have been sorting according to default ls sort, and this affects the order of
// discovery of colors, so be very careful when changing directory/file names being created.
let expected_output : [ ( ColorReference , & str , ColorReference , & str ) ; 6 ] = [
// We don't know what colors are what the first time we meet a link.
( None , " ln-dir3 " , None , " ./dir1/dir2/dir3 " ) ,
// We have acquired [0, 0], which should be the link color,
// and [0, 1], which should be the dir color, and we can compare to them from now on.
( None , " ln-file-invalid " , Some ( [ 1 , 1 ] ) , " dir1/invalid-target " ) ,
// We acquired [1, 1], the non-existent color.
( Some ( [ 0 , 0 ] ) , " ln-file1 " , None , " dir1/file1 " ) ,
( Some ( [ 1 , 1 ] ) , " ln-dir-invalid " , Some ( [ 1 , 1 ] ) , " dir1/dir2 " ) ,
( Some ( [ 0 , 0 ] ) , " ln-root " , Some ( [ 0 , 1 ] ) , " / " ) ,
2022-04-07 09:05:20 +00:00
( Some ( [ 0 , 0 ] ) , " ln-up2 " , None , " ../.. " ) ,
2021-09-05 11:25:56 +00:00
] ;
// We are only interested in lines or the ls output that are symlinks. These start with "lrwx".
let result = scene . ucmd ( ) . arg ( " -laR " ) . arg ( " --color " ) . arg ( " . " ) . succeeds ( ) ;
let mut result_lines = result
. stdout_str ( )
. lines ( )
2021-09-07 17:37:03 +00:00
. filter ( | line | line . starts_with ( " lrwx " ) )
2021-09-05 11:25:56 +00:00
. enumerate ( ) ;
// For each enumerated line, we assert that the output of ls matches the expected output.
//
// The unwraps within get_index_name_target will panic if a line starting lrwx does
// not have `colored_name -> target` within it.
while let Some ( ( i , name , target ) ) = get_index_name_target ( & mut result_lines ) {
// The unwraps within capture_colored_string will panic if the name/target's color
// format is invalid.
2022-04-07 09:05:20 +00:00
dbg! ( & name ) ;
dbg! ( & target ) ;
2021-09-05 11:25:56 +00:00
let ( matched_name_color , matched_name ) = capture_colored_string ( & name ) ;
let ( matched_target_color , matched_target ) = capture_colored_string ( & target ) ;
colors . push ( [ matched_name_color , matched_target_color ] ) ;
// We borrow them again after having moved them. This unwrap will never panic.
let [ matched_name_color , matched_target_color ] = colors . last ( ) . unwrap ( ) ;
// We look up the Colors that are expected in `colors` using the ColorReferences
// stored in `expected_output`.
2021-09-07 17:37:03 +00:00
let expected_name_color = expected_output [ i ]
. 0
. map ( | color_reference | colors [ color_reference [ 0 ] ] [ color_reference [ 1 ] ] . as_str ( ) ) ;
let expected_target_color = expected_output [ i ]
. 2
. map ( | color_reference | colors [ color_reference [ 0 ] ] [ color_reference [ 1 ] ] . as_str ( ) ) ;
2021-09-05 11:25:56 +00:00
// This is the important part. The asserts inside assert_names_and_colors_are_equal
// will panic if the colors or names do not match the expected colors or names.
// Keep in mind an expected color `Option<&str>` of None can mean either that we
// don't expect any color here, as in `expected_output[2], or don't know what specific
// color to expect yet, as in expected_output[0:1].
2022-04-07 09:05:20 +00:00
dbg! ( & colors ) ;
2021-09-05 11:25:56 +00:00
assert_names_and_colors_are_equal (
2021-09-07 17:37:03 +00:00
matched_name_color ,
2021-09-05 11:25:56 +00:00
expected_name_color ,
& matched_name ,
expected_output [ i ] . 1 ,
2021-09-07 17:37:03 +00:00
matched_target_color ,
2021-09-05 11:25:56 +00:00
expected_target_color ,
& matched_target ,
expected_output [ i ] . 3 ,
) ;
}
// End of test, only definitions of the helper functions used above follows...
fn get_index_name_target < ' a , I > ( lines : & mut I ) -> Option < ( usize , Name , Name ) >
where
I : Iterator < Item = ( usize , & ' a str ) > ,
2021-08-30 21:09:16 +00:00
{
2021-09-05 11:25:56 +00:00
match lines . next ( ) {
Some ( ( c , s ) ) = > {
// `name` is whatever comes between \x1b (inclusive) and the arrow.
let name = String ::from ( " \x1b " )
+ s . split ( " -> " )
. next ( )
. unwrap ( )
. split ( " \x1b " )
. last ( )
. unwrap ( ) ;
// `target` is whatever comes after the arrow.
let target = s . split ( " -> " ) . last ( ) . unwrap ( ) . to_string ( ) ;
Some ( ( c , name , target ) )
}
None = > None ,
}
}
2021-09-07 17:37:03 +00:00
#[ allow(clippy::too_many_arguments) ]
2021-09-05 11:25:56 +00:00
fn assert_names_and_colors_are_equal (
name_color : & str ,
expected_name_color : Option < & str > ,
name : & str ,
expected_name : & str ,
target_color : & str ,
expected_target_color : Option < & str > ,
target : & str ,
expected_target : & str ,
) {
// Names are always compared.
assert_eq! ( & name , & expected_name ) ;
assert_eq! ( & target , & expected_target ) ;
// Colors are only compared when we have inferred what color we are looking for.
if expected_name_color . is_some ( ) {
assert_eq! ( & name_color , & expected_name_color . unwrap ( ) ) ;
}
if expected_target_color . is_some ( ) {
assert_eq! ( & target_color , & expected_target_color . unwrap ( ) ) ;
}
}
fn capture_colored_string ( input : & str ) -> ( Color , Name ) {
2023-11-30 10:04:14 +00:00
// Input can be:
// \u{1b}[0m\u{1b}[01;36mln-dir3\u{1b}[0m
// \u{1b}[0m\u{1b}[01;34m./dir1/dir2/dir3\u{1b}[0m
// \u{1b}[0m\u{1b}[01;36mln-file-invalid\u{1b}[0m
// \u{1b}[01;36mdir1/invalid-target\u{1b}[0m
let colored_name = Regex ::new ( r "(?:\x1b\[0m\x1b)?\[([0-9;]+)m(.+)\x1b\[0m" ) . unwrap ( ) ;
2021-09-07 17:37:03 +00:00
match colored_name . captures ( input ) {
2023-11-30 10:04:14 +00:00
Some ( captures ) = > {
dbg! ( captures . get ( 1 ) . unwrap ( ) . as_str ( ) . to_string ( ) ) ;
dbg! ( captures . get ( 2 ) . unwrap ( ) . as_str ( ) . to_string ( ) ) ;
return (
captures . get ( 1 ) . unwrap ( ) . as_str ( ) . to_string ( ) ,
captures . get ( 2 ) . unwrap ( ) . as_str ( ) . to_string ( ) ,
) ;
}
2022-11-30 09:37:59 +00:00
None = > ( String ::new ( ) , input . to_string ( ) ) ,
2021-08-30 21:09:16 +00:00
}
}
2021-09-05 11:25:56 +00:00
fn prepare_folder_structure ( scene : & TestScenario ) {
// There is no way to change directory in the CI, so this is the best we can do.
// Also, keep in mind that windows might require privilege to symlink directories.
//
// We use scene.ccmd instead of scene.fixtures because we care about relative symlinks.
// So we're going to try out the built mkdir, touch, and ln here, and we expect them to succeed.
scene . ccmd ( " mkdir " ) . arg ( " dir1 " ) . succeeds ( ) ;
scene . ccmd ( " mkdir " ) . arg ( " dir1/dir2 " ) . succeeds ( ) ;
scene . ccmd ( " mkdir " ) . arg ( " dir1/dir2/dir3 " ) . succeeds ( ) ;
scene . ccmd ( " touch " ) . arg ( " dir1/file1 " ) . succeeds ( ) ;
scene
. ccmd ( " ln " )
. arg ( " -s " )
. arg ( " dir1/dir2 " )
. arg ( " dir1/ln-dir-invalid " )
. succeeds ( ) ;
scene
. ccmd ( " ln " )
. arg ( " -s " )
. arg ( " ./dir1/dir2/dir3 " )
. arg ( " ln-dir3 " )
. succeeds ( ) ;
scene
. ccmd ( " ln " )
. arg ( " -s " )
. arg ( " ../.. " )
. arg ( " dir1/ln-up2 " )
. succeeds ( ) ;
scene
. ccmd ( " ln " )
. arg ( " -s " )
. arg ( " / " )
. arg ( " dir1/ln-root " )
. succeeds ( ) ;
scene
. ccmd ( " ln " )
. arg ( " -s " )
. arg ( " dir1/file1 " )
. arg ( " ln-file1 " )
. succeeds ( ) ;
scene
. ccmd ( " ln " )
. arg ( " -s " )
. arg ( " dir1/invalid-target " )
. arg ( " ln-file-invalid " )
. succeeds ( ) ;
}
2021-08-30 21:09:16 +00:00
}
2021-05-05 21:03:25 +00:00
#[ test ]
fn test_ls_long_total_size ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-long " ) ) ;
2021-05-05 21:03:25 +00:00
at . append ( " test-long " , " 1 " ) ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-long2 " ) ) ;
2021-05-05 21:03:25 +00:00
at . append ( " test-long2 " , " 2 " ) ;
let expected_prints : HashMap < _ , _ > = if cfg! ( unix ) {
[
( " long_vanilla " , " total 8 " ) ,
( " long_human_readable " , " total 8.0K " ) ,
( " long_si " , " total 8.2k " ) ,
]
. iter ( )
2023-04-20 21:00:46 +00:00
. copied ( )
2021-05-05 21:03:25 +00:00
. collect ( )
} else {
[
( " long_vanilla " , " total 2 " ) ,
( " long_human_readable " , " total 2 " ) ,
( " long_si " , " total 2 " ) ,
]
. iter ( )
2023-04-20 21:00:46 +00:00
. copied ( )
2021-05-05 21:03:25 +00:00
. collect ( )
} ;
2022-01-29 00:03:28 +00:00
for arg in LONG_ARGS {
2021-05-05 21:03:25 +00:00
let result = scene . ucmd ( ) . arg ( arg ) . succeeds ( ) ;
result . stdout_contains ( expected_prints [ " long_vanilla " ] ) ;
2022-04-02 08:47:37 +00:00
for arg2 in [ " -h " , " --human-readable " , " --si " ] {
2021-05-05 21:03:25 +00:00
let result = scene . ucmd ( ) . arg ( arg ) . arg ( arg2 ) . succeeds ( ) ;
2022-04-02 08:47:37 +00:00
result . stdout_contains ( if arg2 = = " --si " {
2021-05-05 21:03:25 +00:00
expected_prints [ " long_si " ]
} else {
expected_prints [ " long_human_readable " ]
} ) ;
}
2020-12-14 20:46:18 +00:00
}
}
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
#[ test ]
fn test_ls_long_formats ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-long-formats " ) ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-09-10 19:35:00 +00:00
// Zero or one "." for indicating a file with security context
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
// Regex for three names, so all of author, group and owner
2023-04-26 11:22:35 +00:00
let re_three = Regex ::new ( r "[xrw-]{9}\.? \d ([-0-9_a-z.A-Z]+ ){3}0" ) . unwrap ( ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-03-26 18:12:01 +00:00
#[ cfg(unix) ]
2021-09-10 19:35:00 +00:00
let re_three_num = Regex ::new ( r "[xrw-]{9}\.? \d (\d+ ){3}0" ) . unwrap ( ) ;
2021-03-26 18:12:01 +00:00
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
// Regex for two names, either:
// - group and owner
// - author and owner
// - author and group
2023-04-26 11:22:35 +00:00
let re_two = Regex ::new ( r "[xrw-]{9}\.? \d ([-0-9_a-z.A-Z]+ ){2}0" ) . unwrap ( ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-03-26 18:12:01 +00:00
#[ cfg(unix) ]
2021-09-10 19:35:00 +00:00
let re_two_num = Regex ::new ( r "[xrw-]{9}\.? \d (\d+ ){2}0" ) . unwrap ( ) ;
2021-03-26 18:12:01 +00:00
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
// Regex for one name: author, group or owner
2023-04-26 11:22:35 +00:00
let re_one = Regex ::new ( r "[xrw-]{9}\.? \d [-0-9_a-z.A-Z]+ 0" ) . unwrap ( ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-03-26 18:12:01 +00:00
#[ cfg(unix) ]
2021-09-10 19:35:00 +00:00
let re_one_num = Regex ::new ( r "[xrw-]{9}\.? \d \d+ 0" ) . unwrap ( ) ;
2021-03-26 18:12:01 +00:00
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
// Regex for no names
2021-09-10 19:35:00 +00:00
let re_zero = Regex ::new ( r "[xrw-]{9}\.? \d 0" ) . unwrap ( ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-09-10 19:35:00 +00:00
scene
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. ucmd ( )
. arg ( " -l " )
. arg ( " --author " )
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_three ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-09-10 19:35:00 +00:00
scene
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. ucmd ( )
. arg ( " -l1 " )
. arg ( " --author " )
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_three ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-03-26 18:12:01 +00:00
#[ cfg(unix) ]
{
2021-09-10 19:35:00 +00:00
scene
2021-03-26 18:12:01 +00:00
. ucmd ( )
. arg ( " -n " )
. arg ( " --author " )
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_three_num ) ;
2021-03-26 18:12:01 +00:00
}
2022-04-02 08:47:37 +00:00
for arg in [
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
" -l " , // only group and owner
" -g --author " , // only author and group
" -o --author " , // only author and owner
" -lG --author " , // only author and owner
" -l --no-group --author " , // only author and owner
] {
2021-09-10 19:35:00 +00:00
scene
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. ucmd ( )
2021-05-29 12:32:35 +00:00
. args ( & arg . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_two ) ;
2021-03-26 18:12:01 +00:00
#[ cfg(unix) ]
{
2021-09-10 19:35:00 +00:00
scene
2021-03-26 18:12:01 +00:00
. ucmd ( )
. arg ( " -n " )
2021-05-29 12:32:35 +00:00
. args ( & arg . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
2021-03-26 18:12:01 +00:00
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_two_num ) ;
2021-03-26 18:12:01 +00:00
}
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
}
2022-04-02 08:47:37 +00:00
for arg in [
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
" -g " , // only group
" -gl " , // only group
" -o " , // only owner
" -ol " , // only owner
" -oG " , // only owner
" -lG " , // only owner
" -l --no-group " , // only owner
" -gG --author " , // only author
] {
2021-09-10 19:35:00 +00:00
scene
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. ucmd ( )
2021-05-29 12:32:35 +00:00
. args ( & arg . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_one ) ;
2021-03-26 18:12:01 +00:00
#[ cfg(unix) ]
{
2021-09-10 19:35:00 +00:00
scene
2021-03-26 18:12:01 +00:00
. ucmd ( )
. arg ( " -n " )
2021-05-29 12:32:35 +00:00
. args ( & arg . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
2021-03-26 18:12:01 +00:00
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_one_num ) ;
2021-03-26 18:12:01 +00:00
}
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
}
2022-04-02 08:47:37 +00:00
for arg in [
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
" -og " ,
" -ogl " ,
" -lgo " ,
" -gG " ,
" -g --no-group " ,
" -og --no-group " ,
" -og --format=long " ,
" -ogCl " ,
" -og --format=vertical -l " ,
" -og1 " ,
" -og1l " ,
] {
2021-09-10 19:35:00 +00:00
scene
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. ucmd ( )
2021-05-29 12:32:35 +00:00
. args ( & arg . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_zero ) ;
2021-03-26 18:12:01 +00:00
#[ cfg(unix) ]
{
2021-09-10 19:35:00 +00:00
scene
2021-03-26 18:12:01 +00:00
. ucmd ( )
. arg ( " -n " )
2021-05-29 12:32:35 +00:00
. args ( & arg . split ( ' ' ) . collect ::< Vec < _ > > ( ) )
2021-03-26 18:12:01 +00:00
. arg ( " test-long-formats " )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_matches ( & re_zero ) ;
2021-03-26 18:12:01 +00:00
}
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
}
}
2021-03-15 12:46:21 +00:00
#[ test ]
fn test_ls_oneline ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-oneline-1 " ) ) ;
at . touch ( at . plus_as_string ( " test-oneline-2 " ) ) ;
2021-03-15 12:46:21 +00:00
// Bit of a weird situation: in the tests oneline and columns have the same output,
// except on Windows.
2022-04-02 08:47:37 +00:00
for option in [ " -1 " , " --format=single-column " ] {
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( option )
. succeeds ( )
. stdout_only ( " test-oneline-1 \n test-oneline-2 \n " ) ;
2021-03-15 12:46:21 +00:00
}
}
2020-12-14 20:46:18 +00:00
#[ test ]
fn test_ls_deref ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
let path_regexp = r "(.*)test-long.link -> (.*)test-long(.*)" ;
let re = Regex ::new ( path_regexp ) . unwrap ( ) ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " test-long " ) ) ;
2020-12-14 20:46:18 +00:00
at . symlink_file ( " test-long " , " test-long.link " ) ;
assert! ( at . is_symlink ( " test-long.link " ) ) ;
let result = scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --color=never " )
. arg ( " test-long " )
. arg ( " test-long.link " )
2021-04-07 09:48:01 +00:00
. succeeds ( ) ;
assert! ( re . is_match ( result . stdout_str ( ) . trim ( ) ) ) ;
2020-12-14 20:46:18 +00:00
let result = scene
. ucmd ( )
. arg ( " -L " )
. arg ( " --color=never " )
. arg ( " test-long " )
. arg ( " test-long.link " )
2021-04-07 09:48:01 +00:00
. succeeds ( ) ;
assert! ( ! re . is_match ( result . stdout_str ( ) . trim ( ) ) ) ;
2020-12-14 20:46:18 +00:00
}
2022-05-21 21:50:32 +00:00
#[ test ]
fn test_ls_group_directories_first ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
let mut filenames = [ " file1 " , " file2 " , " anotherFile " , " abc " , " xxx " , " zzz " ] ;
for filename in filenames {
at . touch ( filename ) ;
}
filenames . sort_unstable ( ) ;
let dirnames = [ " aaa " , " bbb " , " ccc " , " yyy " ] ;
for dirname in dirnames {
at . mkdir ( dirname ) ;
}
let dots = [ " . " , " .. " ] ;
let result = scene
. ucmd ( )
. arg ( " -1a " )
. arg ( " --group-directories-first " )
. run ( ) ;
assert_eq! (
result . stdout_str ( ) . split ( '\n' ) . collect ::< Vec < _ > > ( ) ,
dots . into_iter ( )
. chain ( dirnames . into_iter ( ) )
. chain ( filenames . into_iter ( ) )
. chain ( [ " " ] . into_iter ( ) )
. collect ::< Vec < _ > > ( ) ,
) ;
let result = scene
. ucmd ( )
. arg ( " -1ar " )
. arg ( " --group-directories-first " )
. run ( ) ;
assert_eq! (
result . stdout_str ( ) . split ( '\n' ) . collect ::< Vec < _ > > ( ) ,
( dirnames . into_iter ( ) . rev ( ) )
. chain ( dots . into_iter ( ) . rev ( ) )
. chain ( filenames . into_iter ( ) . rev ( ) )
. chain ( [ " " ] . into_iter ( ) )
. collect ::< Vec < _ > > ( ) ,
) ;
let result = scene
. ucmd ( )
. arg ( " -1aU " )
. arg ( " --group-directories-first " )
. run ( ) ;
let result2 = scene . ucmd ( ) . arg ( " -1aU " ) . run ( ) ;
assert_eq! ( result . stdout_str ( ) , result2 . stdout_str ( ) ) ;
}
2021-04-21 10:03:48 +00:00
#[ test ]
fn test_ls_sort_none ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " test-3 " ) ;
at . touch ( " test-1 " ) ;
at . touch ( " test-2 " ) ;
// Order is not specified so we just check that it doesn't
// give any errors.
scene . ucmd ( ) . arg ( " --sort=none " ) . succeeds ( ) ;
scene . ucmd ( ) . arg ( " -U " ) . succeeds ( ) ;
}
#[ test ]
fn test_ls_sort_name ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " test-3 " ) ;
at . touch ( " test-1 " ) ;
at . touch ( " test-2 " ) ;
scene
. ucmd ( )
. arg ( " --sort=name " )
. succeeds ( )
2021-08-11 20:03:41 +00:00
. stdout_is ( " test-1 \n test-2 \n test-3 \n " ) ;
2021-04-25 19:08:05 +00:00
let scene_dot = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene_dot . fixtures ;
at . touch ( " .a " ) ;
at . touch ( " a " ) ;
at . touch ( " .b " ) ;
at . touch ( " b " ) ;
scene_dot
. ucmd ( )
. arg ( " --sort=name " )
. arg ( " -A " )
. succeeds ( )
2021-08-11 20:03:41 +00:00
. stdout_is ( " .a \n .b \n a \n b \n " ) ;
2021-04-21 10:03:48 +00:00
}
2023-06-06 15:26:42 +00:00
#[ test ]
fn test_ls_sort_width ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " aaaaa " ) ;
at . touch ( " bbb " ) ;
at . touch ( " cccc " ) ;
at . touch ( " eee " ) ;
at . touch ( " d " ) ;
at . touch ( " fffff " ) ;
at . touch ( " abc " ) ;
at . touch ( " zz " ) ;
at . touch ( " bcdef " ) ;
scene
. ucmd ( )
. arg ( " --sort=width " )
. succeeds ( )
. stdout_is ( " d \n zz \n abc \n bbb \n eee \n cccc \n aaaaa \n bcdef \n fffff \n " ) ;
}
2020-12-14 20:46:18 +00:00
#[ test ]
fn test_ls_order_size ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " test-1 " ) ;
at . append ( " test-1 " , " 1 " ) ;
at . touch ( " test-2 " ) ;
at . append ( " test-2 " , " 22 " ) ;
at . touch ( " test-3 " ) ;
at . append ( " test-3 " , " 333 " ) ;
at . touch ( " test-4 " ) ;
at . append ( " test-4 " , " 4444 " ) ;
2021-04-07 09:48:01 +00:00
scene . ucmd ( ) . arg ( " -al " ) . succeeds ( ) ;
2020-12-14 20:46:18 +00:00
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -S " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-4 \n test-3 \n test-2 \n test-1 \n " ) ;
2020-12-14 20:46:18 +00:00
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -S " ) . arg ( " -r " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-1 \n test-2 \n test-3 \n test-4 \n " ) ;
2021-04-21 10:03:48 +00:00
let result = scene . ucmd ( ) . arg ( " --sort=size " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-4 \n test-3 \n test-2 \n test-1 \n " ) ;
2021-04-21 10:03:48 +00:00
let result = scene . ucmd ( ) . arg ( " --sort=size " ) . arg ( " -r " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-1 \n test-2 \n test-3 \n test-4 \n " ) ;
2020-12-14 20:46:18 +00:00
}
#[ test ]
2021-03-15 12:46:21 +00:00
fn test_ls_long_ctime ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " test-long-ctime-1 " ) ;
2022-04-02 08:47:37 +00:00
for arg in [ " -c " , " --time=ctime " , " --time=status " ] {
2021-04-21 10:03:48 +00:00
let result = scene . ucmd ( ) . arg ( " -l " ) . arg ( arg ) . succeeds ( ) ;
// Should show the time on Unix, but question marks on windows.
#[ cfg(unix) ]
result . stdout_contains ( " : " ) ;
#[ cfg(not(unix)) ]
result . stdout_contains ( " ??? " ) ;
}
2021-03-15 12:46:21 +00:00
}
2021-04-28 18:54:27 +00:00
#[ test ]
2021-05-01 13:55:58 +00:00
#[ ignore ]
2021-04-28 18:54:27 +00:00
fn test_ls_order_birthtime ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
/*
Here we make 2 files with a timeout in between .
After creating the first file try to sync it .
This ensures the file gets created immediately instead of being saved
inside the OS ' s IO operation buffer .
2021-04-29 20:23:04 +00:00
Without this , both files might accidentally be created at the same time .
2021-04-28 18:54:27 +00:00
* /
at . make_file ( " test-birthtime-1 " ) . sync_all ( ) . unwrap ( ) ;
2021-04-29 20:23:04 +00:00
at . make_file ( " test-birthtime-2 " ) . sync_all ( ) . unwrap ( ) ;
at . open ( " test-birthtime-1 " ) ;
2021-04-28 18:54:27 +00:00
let result = scene . ucmd ( ) . arg ( " --time=birth " ) . arg ( " -t " ) . run ( ) ;
#[ cfg(not(windows)) ]
assert_eq! ( result . stdout_str ( ) , " test-birthtime-2 \n test-birthtime-1 \n " ) ;
#[ cfg(windows) ]
assert_eq! ( result . stdout_str ( ) , " test-birthtime-2 test-birthtime-1 \n " ) ;
}
#[ test ]
fn test_ls_styles ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " test " ) ;
let re_full = Regex ::new (
2023-04-26 11:22:35 +00:00
r "[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d* (\+|\-)\d{4} test\n" ,
2021-04-28 18:54:27 +00:00
)
. unwrap ( ) ;
let re_long =
2023-04-26 11:22:35 +00:00
Regex ::new ( r "[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2} test\n" ) . unwrap ( ) ;
let re_iso =
Regex ::new ( r "[a-z-]* \d* [\w.]* [\w.]* \d* \d{2}-\d{2} \d{2}:\d{2} test\n" ) . unwrap ( ) ;
2021-04-28 18:54:27 +00:00
let re_locale =
2023-04-26 11:22:35 +00:00
Regex ::new ( r "[a-z-]* \d* [\w.]* [\w.]* \d* [A-Z][a-z]{2} ( |\d)\d \d{2}:\d{2} test\n" )
. unwrap ( ) ;
let re_custom_format =
Regex ::new ( r "[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}__\d{2} test\n" ) . unwrap ( ) ;
2021-04-28 18:54:27 +00:00
//full-iso
2023-04-26 11:22:35 +00:00
scene
2021-04-28 18:54:27 +00:00
. ucmd ( )
. arg ( " -l " )
. arg ( " --time-style=full-iso " )
2023-04-26 11:22:35 +00:00
. succeeds ( )
. stdout_matches ( & re_full ) ;
2021-04-28 18:54:27 +00:00
//long-iso
2023-04-26 11:22:35 +00:00
scene
2021-04-28 18:54:27 +00:00
. ucmd ( )
. arg ( " -l " )
. arg ( " --time-style=long-iso " )
2023-04-26 11:22:35 +00:00
. succeeds ( )
. stdout_matches ( & re_long ) ;
2021-04-28 18:54:27 +00:00
//iso
2023-04-26 11:22:35 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --time-style=iso " )
. succeeds ( )
. stdout_matches ( & re_iso ) ;
2021-04-28 18:54:27 +00:00
//locale
2023-04-26 11:22:35 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --time-style=locale " )
. succeeds ( )
. stdout_matches ( & re_locale ) ;
2021-04-28 18:54:27 +00:00
2022-09-27 21:03:51 +00:00
//+FORMAT
2023-04-26 11:22:35 +00:00
scene
2022-09-27 21:03:51 +00:00
. ucmd ( )
. arg ( " -l " )
. arg ( " --time-style=+%Y__%M " )
2023-04-26 11:22:35 +00:00
. succeeds ( )
. stdout_matches ( & re_custom_format ) ;
2022-09-27 21:03:51 +00:00
// Also fails due to not having full clap support for time_styles
2023-10-24 14:33:04 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --time-style=invalid " )
. fails ( )
. code_is ( 2 ) ;
2022-09-27 21:03:51 +00:00
2021-04-28 18:54:27 +00:00
//Overwrite options tests
2023-04-26 11:22:35 +00:00
scene
2021-04-28 18:54:27 +00:00
. ucmd ( )
. arg ( " -l " )
. arg ( " --time-style=long-iso " )
. arg ( " --time-style=iso " )
2023-04-26 11:22:35 +00:00
. succeeds ( )
. stdout_matches ( & re_iso ) ;
scene
2021-04-28 18:54:27 +00:00
. ucmd ( )
. arg ( " --time-style=iso " )
. arg ( " --full-time " )
2023-04-26 11:22:35 +00:00
. succeeds ( )
. stdout_matches ( & re_full ) ;
scene
2021-04-28 18:54:27 +00:00
. ucmd ( )
. arg ( " --full-time " )
. arg ( " --time-style=iso " )
2023-04-26 11:22:35 +00:00
. succeeds ( )
. stdout_matches ( & re_iso ) ;
2021-04-28 18:54:27 +00:00
2023-04-26 11:22:35 +00:00
scene
2021-04-28 18:54:27 +00:00
. ucmd ( )
. arg ( " --full-time " )
. arg ( " --time-style=iso " )
. arg ( " --full-time " )
2023-04-26 11:22:35 +00:00
. succeeds ( )
. stdout_matches ( & re_full ) ;
2021-04-28 18:54:27 +00:00
2023-04-26 11:22:35 +00:00
scene
2021-04-28 18:54:27 +00:00
. ucmd ( )
. arg ( " --full-time " )
. arg ( " -x " )
. arg ( " -l " )
2023-04-26 11:22:35 +00:00
. succeeds ( )
. stdout_matches ( & re_full ) ;
2021-04-28 18:54:27 +00:00
at . touch ( " test2 " ) ;
2023-04-26 11:22:35 +00:00
scene
. ucmd ( )
. arg ( " --full-time " )
. arg ( " -x " )
. succeeds ( )
. stdout_is ( " test test2 \n " ) ;
2021-04-28 18:54:27 +00:00
}
2021-03-15 12:46:21 +00:00
#[ test ]
fn test_ls_order_time ( ) {
2020-12-14 20:46:18 +00:00
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " test-1 " ) ;
at . append ( " test-1 " , " 1 " ) ;
2021-03-15 12:46:21 +00:00
sleep ( Duration ::from_millis ( 100 ) ) ;
2020-12-14 20:46:18 +00:00
at . touch ( " test-2 " ) ;
at . append ( " test-2 " , " 22 " ) ;
2021-03-22 09:14:59 +00:00
2021-03-15 12:46:21 +00:00
sleep ( Duration ::from_millis ( 100 ) ) ;
2020-12-14 20:46:18 +00:00
at . touch ( " test-3 " ) ;
at . append ( " test-3 " , " 333 " ) ;
2021-03-15 12:46:21 +00:00
sleep ( Duration ::from_millis ( 100 ) ) ;
2020-12-14 20:46:18 +00:00
at . touch ( " test-4 " ) ;
at . append ( " test-4 " , " 4444 " ) ;
2021-03-15 12:46:21 +00:00
sleep ( Duration ::from_millis ( 100 ) ) ;
// Read test-3, only changing access time
at . read ( " test-3 " ) ;
// Set permissions of test-2, only changing ctime
std ::fs ::set_permissions (
at . plus_as_string ( " test-2 " ) ,
at . metadata ( " test-2 " ) . permissions ( ) ,
)
. unwrap ( ) ;
2020-12-14 20:46:18 +00:00
2021-04-07 09:48:01 +00:00
scene . ucmd ( ) . arg ( " -al " ) . succeeds ( ) ;
2020-12-14 20:46:18 +00:00
2021-03-15 12:46:21 +00:00
// ctime was changed at write, so the order is 4 3 2 1
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -t " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-4 \n test-3 \n test-2 \n test-1 \n " ) ;
2020-12-14 20:46:18 +00:00
2021-04-21 10:03:48 +00:00
let result = scene . ucmd ( ) . arg ( " --sort=time " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-4 \n test-3 \n test-2 \n test-1 \n " ) ;
2021-04-21 10:03:48 +00:00
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -tr " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-1 \n test-2 \n test-3 \n test-4 \n " ) ;
2021-03-15 12:46:21 +00:00
2021-04-21 10:03:48 +00:00
let result = scene . ucmd ( ) . arg ( " --sort=time " ) . arg ( " -r " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-1 \n test-2 \n test-3 \n test-4 \n " ) ;
2021-04-21 10:03:48 +00:00
2021-03-15 12:46:21 +00:00
// 3 was accessed last in the read
// So the order should be 2 3 4 1
2022-04-02 08:47:37 +00:00
for arg in [ " -u " , " --time=atime " , " --time=access " , " --time=use " ] {
2021-04-21 10:03:48 +00:00
let result = scene . ucmd ( ) . arg ( " -t " ) . arg ( arg ) . succeeds ( ) ;
2022-02-02 04:49:40 +00:00
at . open ( " test-3 " ) . metadata ( ) . unwrap ( ) . accessed ( ) . unwrap ( ) ;
at . open ( " test-4 " ) . metadata ( ) . unwrap ( ) . accessed ( ) . unwrap ( ) ;
2021-04-21 10:03:48 +00:00
// It seems to be dependent on the platform whether the access time is actually set
2023-04-25 12:14:09 +00:00
#[ cfg(unix) ]
{
let expected = unwrap_or_return! ( expected_result ( & scene , & [ " -t " , arg ] ) ) ;
at . open ( " test-3 " ) . metadata ( ) . unwrap ( ) . accessed ( ) . unwrap ( ) ;
at . open ( " test-4 " ) . metadata ( ) . unwrap ( ) . accessed ( ) . unwrap ( ) ;
result . stdout_only ( expected . stdout_str ( ) ) ;
}
#[ cfg(windows) ]
2022-02-02 04:49:40 +00:00
result . stdout_only ( " test-4 \n test-3 \n test-2 \n test-1 \n " ) ;
2021-03-22 09:14:59 +00:00
}
2021-03-15 12:46:21 +00:00
// test-2 had the last ctime change when the permissions were set
// So the order should be 2 4 3 1
#[ cfg(unix) ]
{
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -tc " ) . succeeds ( ) ;
2021-08-11 20:03:41 +00:00
result . stdout_only ( " test-2 \n test-4 \n test-3 \n test-1 \n " ) ;
2021-03-15 12:46:21 +00:00
}
2020-12-14 20:46:18 +00:00
}
2020-12-13 11:09:14 +00:00
#[ test ]
fn test_ls_non_existing ( ) {
new_ucmd! ( ) . arg ( " doesntexist " ) . fails ( ) ;
}
#[ test ]
fn test_ls_files_dirs ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " a " ) ;
at . mkdir ( " a/b " ) ;
at . mkdir ( " a/b/c " ) ;
at . mkdir ( " z " ) ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " a/a " ) ) ;
at . touch ( at . plus_as_string ( " a/b/b " ) ) ;
2020-12-13 11:09:14 +00:00
scene . ucmd ( ) . arg ( " a " ) . succeeds ( ) ;
scene . ucmd ( ) . arg ( " a/a " ) . succeeds ( ) ;
scene . ucmd ( ) . arg ( " a " ) . arg ( " z " ) . succeeds ( ) ;
// Doesn't exist
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " doesntexist " )
. fails ( )
2022-10-12 20:04:21 +00:00
. stderr_contains ( " 'doesntexist': No such file or directory " ) ;
2020-12-13 11:09:14 +00:00
// One exists, the other doesn't
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " a " )
. arg ( " doesntexist " )
. fails ( )
2022-10-12 20:04:21 +00:00
. stderr_contains ( " 'doesntexist': No such file or directory " )
. stdout_contains ( " a: " ) ;
2020-12-13 11:09:14 +00:00
}
2020-12-13 11:22:31 +00:00
#[ test ]
fn test_ls_recursive ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " a " ) ;
at . mkdir ( " a/b " ) ;
at . mkdir ( " a/b/c " ) ;
at . mkdir ( " z " ) ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " a/a " ) ) ;
at . touch ( at . plus_as_string ( " a/b/b " ) ) ;
2020-12-13 11:22:31 +00:00
scene . ucmd ( ) . arg ( " a " ) . succeeds ( ) ;
scene . ucmd ( ) . arg ( " a/a " ) . succeeds ( ) ;
2021-05-30 12:30:52 +00:00
scene
. ucmd ( )
. arg ( " z " )
. arg ( " -R " )
. succeeds ( )
2022-10-12 20:04:21 +00:00
. stdout_contains ( " z: " ) ;
2020-12-13 11:22:31 +00:00
let result = scene
. ucmd ( )
. arg ( " --color=never " )
. arg ( " -R " )
. arg ( " a " )
. arg ( " z " )
. succeeds ( ) ;
2020-12-14 20:46:18 +00:00
#[ cfg(not(windows)) ]
2022-10-12 20:04:21 +00:00
result . stdout_contains ( " a/b: \n b " ) ;
2020-12-14 20:46:18 +00:00
#[ cfg(windows) ]
2022-10-23 04:18:56 +00:00
result . stdout_contains ( " a \\ b: \n b " ) ;
2020-12-13 11:22:31 +00:00
}
2023-10-01 07:58:09 +00:00
#[ test ]
fn test_ls_recursive_1 ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " x " ) ;
at . mkdir ( " y " ) ;
at . mkdir ( " a " ) ;
at . mkdir ( " b " ) ;
at . mkdir ( " c " ) ;
at . mkdir ( " a/1 " ) ;
at . mkdir ( " a/2 " ) ;
at . mkdir ( " a/3 " ) ;
at . touch ( " f " ) ;
at . touch ( " a/1/I " ) ;
at . touch ( " a/1/II " ) ;
#[ cfg(unix) ]
let out = " a: \n 1 \n 2 \n 3 \n \n a/1: \n I \n II \n \n a/2: \n \n a/3: \n \n b: \n \n c: \n " ;
#[ cfg(windows) ]
let out = " a: \n 1 \n 2 \n 3 \n \n a \\ 1: \n I \n II \n \n a \\ 2: \n \n a \\ 3: \n \n b: \n \n c: \n " ;
scene
. ucmd ( )
. arg ( " -R1 " )
. arg ( " a " )
. arg ( " b " )
. arg ( " c " )
. succeeds ( )
. stdout_is ( out ) ;
}
ls: implement --color flag
GNU coreutils ls command implements the --color option as follow:
--color[=WHEN]
colorize the output; WHEN can be 'always' (default if omitted),
'auto', or 'never'
With --color=auto, ls emits color codes only when standard output is connected
to a terminal.
Also, add support for the following aliases:
- ‘always’, ‘yes’, ‘force’
- ‘never’, ‘no’, ‘none’
- ‘auto’, ‘tty’, ‘if-tty’
Signed-off-by: Gabriel Ganne <gabriel.ganne@gmail.com>
2019-06-20 07:55:01 +00:00
#[ test ]
2021-04-21 15:57:17 +00:00
fn test_ls_color ( ) {
2020-12-14 20:46:18 +00:00
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " a " ) ;
2021-04-21 14:58:37 +00:00
let nested_dir = Path ::new ( " a " )
. join ( " nested_dir " )
. to_string_lossy ( )
. to_string ( ) ;
2023-11-16 15:02:38 +00:00
at . mkdir ( nested_dir ) ;
2020-12-14 20:46:18 +00:00
at . mkdir ( " z " ) ;
2021-04-21 14:58:37 +00:00
let nested_file = Path ::new ( " a " )
. join ( " nested_file " )
. to_string_lossy ( )
. to_string ( ) ;
2023-02-13 09:00:03 +00:00
at . touch ( nested_file ) ;
2021-03-17 22:15:03 +00:00
at . touch ( " test-color " ) ;
2023-11-30 10:04:14 +00:00
let a_with_colors = " \x1b [0m \x1b [01;34ma \x1b [0m " ;
let z_with_colors = " \x1b [01;34mz \x1b [0m \n " ;
let nested_dir_with_colors = " \x1b [0m \x1b [01;34mnested_dir \x1b [0m \x0a nested_file " ; // spell-checker:disable-line
2021-03-17 22:15:03 +00:00
// Color is disabled by default
let result = scene . ucmd ( ) . succeeds ( ) ;
2021-04-07 09:48:01 +00:00
assert! ( ! result . stdout_str ( ) . contains ( a_with_colors ) ) ;
assert! ( ! result . stdout_str ( ) . contains ( z_with_colors ) ) ;
2021-03-17 22:15:03 +00:00
// Color should be enabled
2022-04-02 08:47:37 +00:00
for param in [ " --color " , " --col " , " --color=always " , " --col=always " ] {
2023-11-30 10:04:14 +00:00
let result = scene . ucmd ( ) . arg ( param ) . succeeds ( ) ;
assert! ( result . stdout_str ( ) . contains ( a_with_colors ) ) ;
assert! ( result . stdout_str ( ) . contains ( z_with_colors ) ) ;
2022-01-29 00:03:28 +00:00
}
2021-03-17 22:15:03 +00:00
// Color should be disabled
let result = scene . ucmd ( ) . arg ( " --color=never " ) . succeeds ( ) ;
2021-04-07 09:48:01 +00:00
assert! ( ! result . stdout_str ( ) . contains ( a_with_colors ) ) ;
assert! ( ! result . stdout_str ( ) . contains ( z_with_colors ) ) ;
2021-03-17 22:15:03 +00:00
// Nested dir should be shown and colored
2023-11-30 10:04:14 +00:00
let result = scene . ucmd ( ) . arg ( " --color " ) . arg ( " a " ) . succeeds ( ) ;
assert! ( result . stdout_str ( ) . contains ( nested_dir_with_colors ) ) ;
2021-03-17 22:15:03 +00:00
// No output
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " --color=never " )
. arg ( " z " )
. succeeds ( )
. stdout_only ( " " ) ;
2021-04-26 12:42:42 +00:00
// The colors must not mess up the grid layout
at . touch ( " b " ) ;
2023-11-30 10:04:14 +00:00
let result = scene
2021-04-26 12:42:42 +00:00
. ucmd ( )
. arg ( " --color " )
. arg ( " -w=15 " )
2021-08-11 20:03:41 +00:00
. arg ( " -C " )
2023-11-30 10:04:14 +00:00
. succeeds ( ) ;
let expected = format! ( " {} test-color \x0a b {} " , a_with_colors , z_with_colors ) ;
assert_eq! (
2023-12-09 08:53:50 +00:00
result . stdout_str ( ) . escape_default ( ) . to_string ( ) ,
expected . escape_default ( ) . to_string ( )
2023-11-30 10:04:14 +00:00
) ;
assert_eq! ( result . stdout_str ( ) , expected ) ;
ls: implement --color flag
GNU coreutils ls command implements the --color option as follow:
--color[=WHEN]
colorize the output; WHEN can be 'always' (default if omitted),
'auto', or 'never'
With --color=auto, ls emits color codes only when standard output is connected
to a terminal.
Also, add support for the following aliases:
- ‘always’, ‘yes’, ‘force’
- ‘never’, ‘no’, ‘none’
- ‘auto’, ‘tty’, ‘if-tty’
Signed-off-by: Gabriel Ganne <gabriel.ganne@gmail.com>
2019-06-20 07:55:01 +00:00
}
2020-12-15 12:36:12 +00:00
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
#[ cfg(unix) ]
#[ test ]
fn test_ls_inode ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
let file = " test_inode " ;
at . touch ( file ) ;
let re_short = Regex ::new ( r " *(\d+) test_inode" ) . unwrap ( ) ;
2021-09-10 19:35:00 +00:00
let re_long = Regex ::new ( r " *(\d+) [xrw-]{10}\.? \d .+ test_inode" ) . unwrap ( ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " test_inode " ) . arg ( " -i " ) . succeeds ( ) ;
assert! ( re_short . is_match ( result . stdout_str ( ) ) ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
let inode_short = re_short
2021-04-07 09:48:01 +00:00
. captures ( result . stdout_str ( ) )
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. unwrap ( )
. get ( 1 )
. unwrap ( )
. as_str ( ) ;
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " test_inode " ) . succeeds ( ) ;
assert! ( ! re_short . is_match ( result . stdout_str ( ) ) ) ;
assert! ( ! result . stdout_str ( ) . contains ( inode_short ) ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -li " ) . arg ( " test_inode " ) . succeeds ( ) ;
assert! ( re_long . is_match ( result . stdout_str ( ) ) ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
let inode_long = re_long
2021-04-07 09:48:01 +00:00
. captures ( result . stdout_str ( ) )
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
. unwrap ( )
. get ( 1 )
. unwrap ( )
. as_str ( ) ;
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -l " ) . arg ( " test_inode " ) . succeeds ( ) ;
assert! ( ! re_long . is_match ( result . stdout_str ( ) ) ) ;
assert! ( ! result . stdout_str ( ) . contains ( inode_long ) ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
2022-01-30 12:55:03 +00:00
assert_eq! ( inode_short , inode_long ) ;
ls: long format author, group and owner (#1850)
This PR adds the options to customize what information is shown in long format regarding author, group & owner. Specifically it adds:
- `--author`: shows the author, which is always the same as the owner. GNU has this feature because GNU/Hurd supports a difference between author and owner, but I don't think Rust supports GNU/Hurd, so I just used the owner.
- `-G` & `--no-group`: hide the group information.
- `-o`: hide the group and use long format (equivalent to `-lG`).
- `-g`: hide the owner and use long format.
The `-o` and `-g` options have some interesting behaviour that I had to account for. Some examples:
- `-og` hides both group and owner.
- `-ol` still hides the group. Same behaviour with variations such as `-o --format=long`, `-gl`, `-g --format=long` and `-ogl`.
- They even retain some information when overridden by another format: `-oCl` (or `-o --format=vertical --format=long`) still hides the group.
My previous solution for handling the behaviour where `-l1` shows the long format did not fit with these additions, so I had to rewrite that as well.
The tests only cover the how many names (author, group and owner) are present in the output, so it can't distinguish between, for example, author & group and group & owner.
2021-03-21 15:18:06 +00:00
}
2021-03-29 11:10:13 +00:00
#[ test ]
#[ cfg(not(windows)) ]
fn test_ls_indicator_style ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
// Setup: Directory, Symlink, and Pipes.
at . mkdir ( " directory " ) ;
assert! ( at . dir_exists ( " directory " ) ) ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " link-src " ) ) ;
2021-03-29 11:10:13 +00:00
at . symlink_file ( " link-src " , " link-dest.link " ) ;
assert! ( at . is_symlink ( " link-dest.link " ) ) ;
at . mkfifo ( " named-pipe.fifo " ) ;
assert! ( at . is_fifo ( " named-pipe.fifo " ) ) ;
// Classify, File-Type, and Slash all contain indicators for directories.
2022-01-29 00:03:28 +00:00
for opt in [
" --indicator-style=classify " ,
" --ind=classify " ,
" --indicator-style=file-type " ,
" --ind=file-type " ,
" --indicator-style=slash " ,
" --ind=slash " ,
" --classify " ,
2022-02-10 20:35:20 +00:00
" --classify=always " ,
" --classify=yes " ,
" --classify=force " ,
2022-01-29 00:03:28 +00:00
" --class " ,
" --file-type " ,
" --file " ,
" -p " ,
] {
2021-03-29 11:10:13 +00:00
// Verify that classify and file-type both contain indicators for symlinks.
2022-10-12 20:04:21 +00:00
scene . ucmd ( ) . arg ( opt ) . succeeds ( ) . stdout_contains ( " / " ) ;
2021-03-29 11:10:13 +00:00
}
2022-02-10 20:35:20 +00:00
// Classify, Indicator options should not contain any indicators when value is none.
for opt in [
" --indicator-style=none " ,
" --ind=none " ,
" --classify=none " ,
" --classify=never " ,
" --classify=no " ,
] {
// Verify that there are no indicators for any of the file types.
scene
. ucmd ( )
. arg ( opt )
. succeeds ( )
2022-10-12 20:04:21 +00:00
. stdout_does_not_contain ( " / " )
. stdout_does_not_contain ( " @ " )
. stdout_does_not_contain ( " | " ) ;
2022-02-10 20:35:20 +00:00
}
2021-03-29 11:10:13 +00:00
// Classify and File-Type all contain indicators for pipes and links.
let options = vec! [ " classify " , " file-type " ] ;
for opt in options {
// Verify that classify and file-type both contain indicators for symlinks.
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " --indicator-style= {opt} " ) )
2021-04-07 09:48:01 +00:00
. succeeds ( )
2022-10-12 20:04:21 +00:00
. stdout_contains ( " @ " )
. stdout_contains ( " | " ) ;
2021-03-29 11:10:13 +00:00
}
// Test sockets. Because the canonical way of making sockets to test is with
// TempDir, we need a separate test.
{
2022-08-20 14:02:29 +00:00
use std ::os ::unix ::net ::UnixListener ;
2021-03-29 11:10:13 +00:00
2021-05-29 12:32:35 +00:00
let dir = tempfile ::Builder ::new ( )
. prefix ( " unix_socket " )
. tempdir ( )
. expect ( " failed to create dir " ) ;
2021-03-29 11:10:13 +00:00
let socket_path = dir . path ( ) . join ( " sock " ) ;
2022-11-15 15:57:08 +00:00
let _listener = UnixListener ::bind ( socket_path ) . expect ( " failed to create socket " ) ;
2021-03-29 11:10:13 +00:00
new_ucmd! ( )
. args ( & [
PathBuf ::from ( dir . path ( ) . to_str ( ) . unwrap ( ) ) ,
PathBuf ::from ( " --indicator-style=classify " ) ,
] )
. succeeds ( )
. stdout_only ( " sock= \n " ) ;
}
}
// Essentially the same test as above, but only test symlinks and directories,
// not pipes or sockets.
#[ test ]
#[ cfg(not(unix)) ]
fn test_ls_indicator_style ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
// Setup: Directory, Symlink.
at . mkdir ( " directory " ) ;
assert! ( at . dir_exists ( " directory " ) ) ;
2023-02-13 09:00:03 +00:00
at . touch ( at . plus_as_string ( " link-src " ) ) ;
2021-03-29 11:10:13 +00:00
at . symlink_file ( " link-src " , " link-dest.link " ) ;
assert! ( at . is_symlink ( " link-dest.link " ) ) ;
// Classify, File-Type, and Slash all contain indicators for directories.
let options = vec! [ " classify " , " file-type " , " slash " ] ;
for opt in options {
// Verify that classify and file-type both contain indicators for symlinks.
2021-04-21 15:43:57 +00:00
scene
2021-04-07 09:48:01 +00:00
. ucmd ( )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " --indicator-style= {opt} " ) )
2021-04-07 09:48:01 +00:00
. succeeds ( )
2022-10-23 04:18:56 +00:00
. stdout_contains ( " / " ) ;
2021-03-29 11:10:13 +00:00
}
// Same test as above, but with the alternate flags.
let options = vec! [ " --classify " , " --file-type " , " -p " ] ;
for opt in options {
2022-10-23 04:18:56 +00:00
scene . ucmd ( ) . arg ( opt ) . succeeds ( ) . stdout_contains ( " / " ) ;
2021-03-29 11:10:13 +00:00
}
// Classify and File-Type all contain indicators for pipes and links.
let options = vec! [ " classify " , " file-type " ] ;
for opt in options {
// Verify that classify and file-type both contain indicators for symlinks.
2021-04-21 15:43:57 +00:00
scene
2021-04-07 09:48:01 +00:00
. ucmd ( )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " --indicator-style= {opt} " ) )
2021-04-07 09:48:01 +00:00
. succeeds ( )
2022-10-23 04:18:56 +00:00
. stdout_contains ( " @ " ) ;
2021-03-29 11:10:13 +00:00
}
}
2021-03-18 01:32:34 +00:00
#[ cfg(not(any(target_vendor = " apple " , target_os = " windows " ))) ] // Truncate not available on mac or win
2020-12-15 12:36:12 +00:00
#[ test ]
2021-03-19 14:15:24 +00:00
fn test_ls_human_si ( ) {
2020-12-15 12:36:12 +00:00
let scene = TestScenario ::new ( util_name! ( ) ) ;
2021-03-19 14:15:24 +00:00
let file1 = " test_human-1 " ;
2021-04-07 09:48:01 +00:00
scene
2021-03-19 14:15:24 +00:00
. cmd ( " truncate " )
. arg ( " -s " )
. arg ( " +1000 " )
. arg ( file1 )
2021-04-07 09:48:01 +00:00
. succeeds ( ) ;
2021-03-19 14:15:24 +00:00
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -hl " )
. arg ( file1 )
. succeeds ( )
. stdout_contains ( " 1000 " ) ;
2021-03-19 14:15:24 +00:00
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --si " )
. arg ( file1 )
. succeeds ( )
. stdout_contains ( " 1.0k " ) ;
2021-03-19 14:15:24 +00:00
2020-12-17 19:50:22 +00:00
scene
. cmd ( " truncate " )
. arg ( " -s " )
. arg ( " +1000k " )
2021-03-19 14:15:24 +00:00
. arg ( file1 )
. run ( ) ;
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -hl " )
. arg ( file1 )
. succeeds ( )
. stdout_contains ( " 1001K " ) ;
2021-03-19 14:15:24 +00:00
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --si " )
. arg ( file1 )
. succeeds ( )
. stdout_contains ( " 1.1M " ) ;
2021-03-19 14:15:24 +00:00
let file2 = " test-human-2 " ;
2021-04-07 09:48:01 +00:00
scene
2021-03-19 14:15:24 +00:00
. cmd ( " truncate " )
. arg ( " -s " )
. arg ( " +12300k " )
. arg ( file2 )
2021-04-07 09:48:01 +00:00
. succeeds ( ) ;
2021-03-19 14:15:24 +00:00
// GNU rounds up, so we must too.
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -hl " )
. arg ( file2 )
. succeeds ( )
. stdout_contains ( " 13M " ) ;
2021-03-19 14:15:24 +00:00
// GNU rounds up, so we must too.
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --si " )
. arg ( file2 )
. succeeds ( )
. stdout_contains ( " 13M " ) ;
2021-03-19 14:15:24 +00:00
let file3 = " test-human-3 " ;
2021-04-07 09:48:01 +00:00
scene
2021-03-19 14:15:24 +00:00
. cmd ( " truncate " )
. arg ( " -s " )
. arg ( " +9999 " )
. arg ( file3 )
2021-04-07 09:48:01 +00:00
. succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -hl " )
. arg ( file3 )
. succeeds ( )
. stdout_contains ( " 9.8K " ) ;
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --si " )
. arg ( file3 )
. succeeds ( )
. stdout_contains ( " 10k " ) ;
2020-12-15 12:36:12 +00:00
}
2020-12-19 16:54:28 +00:00
#[ cfg(windows) ]
#[ test ]
fn test_ls_hidden_windows ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
let file = " hiddenWindowsFileNoDot " ;
at . touch ( file ) ;
// hide the file
scene
. cmd ( " attrib " )
. arg ( " +h " )
. arg ( " +S " )
. arg ( " +r " )
. arg ( file )
2021-04-07 09:48:01 +00:00
. succeeds ( ) ;
let result = scene . ucmd ( ) . succeeds ( ) ;
assert! ( ! result . stdout_str ( ) . contains ( file ) ) ;
2021-04-21 15:43:57 +00:00
scene . ucmd ( ) . arg ( " -a " ) . succeeds ( ) . stdout_contains ( file ) ;
2020-12-19 16:54:28 +00:00
}
2021-03-25 19:24:53 +00:00
2022-01-08 23:36:01 +00:00
#[ cfg(windows) ]
#[ test ]
fn test_ls_hidden_link_windows ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
let file = " visibleWindowsFileNoDot " ;
at . touch ( file ) ;
let link = " hiddenWindowsLinkNoDot " ;
at . symlink_dir ( file , link ) ;
// hide the link
scene . cmd ( " attrib " ) . arg ( " /l " ) . arg ( " +h " ) . arg ( link ) . succeeds ( ) ;
scene
. ucmd ( )
. succeeds ( )
. stdout_contains ( file )
. stdout_does_not_contain ( link ) ;
scene
. ucmd ( )
. arg ( " -a " )
. succeeds ( )
. stdout_contains ( file )
. stdout_contains ( link ) ;
}
#[ cfg(windows) ]
#[ test ]
fn test_ls_success_on_c_drv_root_windows ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene . ucmd ( ) . arg ( " C: \\ " ) . succeeds ( ) ;
}
2021-03-25 19:24:53 +00:00
#[ test ]
fn test_ls_version_sort ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2022-04-02 08:47:37 +00:00
for filename in [
2023-07-09 22:29:42 +00:00
" a2 " ,
" b1 " ,
" b20 " ,
" a1.4 " ,
" a1.40 " ,
" b3 " ,
" b11 " ,
" b20b " ,
" b20a " ,
" a100 " ,
" a1.13 " ,
" aa " ,
" a1 " ,
" aaa " ,
" a1.00000040 " ,
" abab " ,
" ab " ,
" a01.40 " ,
" a001.001 " ,
" a01.0000001 " ,
" a01.001 " ,
" a001.01 " ,
2021-03-25 19:24:53 +00:00
] {
at . touch ( filename ) ;
}
let mut expected = vec! [
2023-07-09 22:29:42 +00:00
" a1 " ,
" a001.001 " ,
" a001.01 " ,
" a01.0000001 " ,
" a01.001 " ,
" a1.4 " ,
" a1.13 " ,
" a01.40 " ,
" a1.00000040 " ,
" a1.40 " ,
" a2 " ,
" a100 " ,
" aa " ,
" aaa " ,
" ab " ,
" abab " ,
" b1 " ,
" b3 " ,
" b11 " ,
" b20 " ,
" b20a " ,
" b20b " ,
" " , // because of '\n' at the end of the output
2021-03-25 19:24:53 +00:00
] ;
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -1v " ) . succeeds ( ) ;
assert_eq! (
result . stdout_str ( ) . split ( '\n' ) . collect ::< Vec < _ > > ( ) ,
expected
) ;
2021-03-25 19:24:53 +00:00
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -1 " ) . arg ( " --sort=version " ) . succeeds ( ) ;
assert_eq! (
result . stdout_str ( ) . split ( '\n' ) . collect ::< Vec < _ > > ( ) ,
expected
) ;
2021-03-25 19:24:53 +00:00
2021-04-07 09:48:01 +00:00
let result = scene . ucmd ( ) . arg ( " -a1v " ) . succeeds ( ) ;
2022-05-21 21:38:31 +00:00
expected . insert ( expected . len ( ) - 1 , " .. " ) ;
2021-03-25 19:24:53 +00:00
expected . insert ( 0 , " . " ) ;
2021-04-07 09:48:01 +00:00
assert_eq! (
result . stdout_str ( ) . split ( '\n' ) . collect ::< Vec < _ > > ( ) ,
expected ,
2022-01-30 12:55:03 +00:00
) ;
2021-03-25 19:24:53 +00:00
}
2021-04-01 20:50:13 +00:00
#[ test ]
fn test_ls_quoting_style ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " one two " ) ;
at . touch ( " one " ) ;
// It seems that windows doesn't allow \n in filenames.
2021-04-21 10:45:21 +00:00
// And it also doesn't like \, of course.
2021-04-01 20:50:13 +00:00
#[ cfg(unix) ]
{
at . touch ( " one \n two " ) ;
2021-04-21 10:45:21 +00:00
at . touch ( " one \\ two " ) ;
2023-12-10 15:07:41 +00:00
// Default is literal, when stdout is not a TTY.
// Otherwise, it is shell-escape
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
2021-08-31 10:55:17 +00:00
. arg ( " --hide-control-chars " )
2021-04-07 09:48:01 +00:00
. arg ( " one \n two " )
. succeeds ( )
2023-12-10 15:07:41 +00:00
. stdout_only ( " one?two \n " ) ;
// TODO: TTY-expected output, find a way to check this as well
// .stdout_only("'one'$'\\n''two'\n");
2021-04-01 20:50:13 +00:00
2022-04-02 08:47:37 +00:00
for ( arg , correct ) in [
2021-04-03 11:15:19 +00:00
( " --quoting-style=literal " , " one?two " ) ,
( " -N " , " one?two " ) ,
( " --literal " , " one?two " ) ,
2023-07-08 01:43:20 +00:00
( " --l " , " one?two " ) ,
2021-04-01 20:50:13 +00:00
( " --quoting-style=c " , " \" one \\ ntwo \" " ) ,
( " -Q " , " \" one \\ ntwo \" " ) ,
( " --quote-name " , " \" one \\ ntwo \" " ) ,
( " --quoting-style=escape " , " one \\ ntwo " ) ,
( " -b " , " one \\ ntwo " ) ,
( " --escape " , " one \\ ntwo " ) ,
( " --quoting-style=shell-escape " , " 'one'$' \\ n''two' " ) ,
( " --quoting-style=shell-escape-always " , " 'one'$' \\ n''two' " ) ,
( " --quoting-style=shell " , " one?two " ) ,
( " --quoting-style=shell-always " , " 'one?two' " ) ,
] {
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
2021-04-03 14:42:29 +00:00
. arg ( " --hide-control-chars " )
2021-08-31 10:55:17 +00:00
. arg ( arg )
2021-04-03 14:42:29 +00:00
. arg ( " one \n two " )
2021-04-07 09:48:01 +00:00
. succeeds ( )
2023-01-27 09:29:45 +00:00
. stdout_only ( format! ( " {correct} \n " ) ) ;
2021-04-03 14:42:29 +00:00
}
2022-04-02 08:47:37 +00:00
for ( arg , correct ) in [
2021-04-03 11:15:19 +00:00
( " --quoting-style=literal " , " one \n two " ) ,
( " -N " , " one \n two " ) ,
( " --literal " , " one \n two " ) ,
2023-07-08 01:43:20 +00:00
( " --l " , " one \n two " ) ,
2021-08-31 10:55:17 +00:00
( " --quoting-style=shell " , " one \n two " ) , // FIXME: GNU ls quotes this case
2021-04-03 11:15:19 +00:00
( " --quoting-style=shell-always " , " 'one \n two' " ) ,
] {
2021-04-07 09:48:01 +00:00
scene
2021-04-03 11:15:19 +00:00
. ucmd ( )
. arg ( arg )
. arg ( " --show-control-chars " )
. arg ( " one \n two " )
2021-04-07 09:48:01 +00:00
. succeeds ( )
2023-01-27 09:29:45 +00:00
. stdout_only ( format! ( " {correct} \n " ) ) ;
2021-04-03 11:15:19 +00:00
}
2021-04-21 10:45:21 +00:00
2022-04-02 08:47:37 +00:00
for ( arg , correct ) in [
2021-04-21 10:45:21 +00:00
( " --quoting-style=literal " , " one \\ two " ) ,
( " -N " , " one \\ two " ) ,
( " --quoting-style=c " , " \" one \\ \\ two \" " ) ,
( " -Q " , " \" one \\ \\ two \" " ) ,
( " --quote-name " , " \" one \\ \\ two \" " ) ,
( " --quoting-style=escape " , " one \\ \\ two " ) ,
( " -b " , " one \\ \\ two " ) ,
( " --quoting-style=shell-escape " , " 'one \\ two' " ) ,
( " --quoting-style=shell-escape-always " , " 'one \\ two' " ) ,
( " --quoting-style=shell " , " 'one \\ two' " ) ,
( " --quoting-style=shell-always " , " 'one \\ two' " ) ,
] {
scene
. ucmd ( )
2021-08-31 10:55:17 +00:00
. arg ( " --hide-control-chars " )
2021-04-21 10:45:21 +00:00
. arg ( arg )
. arg ( " one \\ two " )
. succeeds ( )
2023-01-27 09:29:45 +00:00
. stdout_only ( format! ( " {correct} \n " ) ) ;
2021-04-21 10:45:21 +00:00
}
// Tests for a character that forces quotation in shell-style escaping
// after a character in a dollar expression
at . touch ( " one \n &two " ) ;
2022-04-02 08:47:37 +00:00
for ( arg , correct ) in [
2021-04-21 10:45:21 +00:00
( " --quoting-style=shell-escape " , " 'one'$' \\ n''&two' " ) ,
( " --quoting-style=shell-escape-always " , " 'one'$' \\ n''&two' " ) ,
] {
scene
. ucmd ( )
2021-08-31 10:55:17 +00:00
. arg ( " --hide-control-chars " )
2021-04-21 10:45:21 +00:00
. arg ( arg )
. arg ( " one \n &two " )
. succeeds ( )
2023-01-27 09:29:45 +00:00
. stdout_only ( format! ( " {correct} \n " ) ) ;
2021-04-21 10:45:21 +00:00
}
2021-04-01 20:50:13 +00:00
}
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
. arg ( " one two " )
. succeeds ( )
2023-12-10 15:07:41 +00:00
. stdout_only ( " one two \n " ) ;
// TODO: TTY-expected output
// .stdout_only("'one two'\n");
2021-04-01 20:50:13 +00:00
2022-04-02 08:47:37 +00:00
for ( arg , correct ) in [
2021-04-01 20:50:13 +00:00
( " --quoting-style=literal " , " one two " ) ,
( " -N " , " one two " ) ,
( " --literal " , " one two " ) ,
2023-07-08 01:43:20 +00:00
( " --l " , " one two " ) ,
2021-04-01 20:50:13 +00:00
( " --quoting-style=c " , " \" one two \" " ) ,
( " -Q " , " \" one two \" " ) ,
( " --quote-name " , " \" one two \" " ) ,
( " --quoting-style=escape " , " one \\ two " ) ,
( " -b " , " one \\ two " ) ,
( " --escape " , " one \\ two " ) ,
( " --quoting-style=shell-escape " , " 'one two' " ) ,
( " --quoting-style=shell-escape-always " , " 'one two' " ) ,
( " --quoting-style=shell " , " 'one two' " ) ,
( " --quoting-style=shell-always " , " 'one two' " ) ,
] {
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
2021-08-31 10:55:17 +00:00
. arg ( " --hide-control-chars " )
2021-04-07 09:48:01 +00:00
. arg ( arg )
. arg ( " one two " )
. succeeds ( )
2023-01-27 09:29:45 +00:00
. stdout_only ( format! ( " {correct} \n " ) ) ;
2021-04-01 20:50:13 +00:00
}
2021-04-07 09:48:01 +00:00
scene . ucmd ( ) . arg ( " one " ) . succeeds ( ) . stdout_only ( " one \n " ) ;
2021-04-01 20:50:13 +00:00
2022-04-02 08:47:37 +00:00
for ( arg , correct ) in [
2021-04-01 20:50:13 +00:00
( " --quoting-style=literal " , " one " ) ,
( " -N " , " one " ) ,
( " --quoting-style=c " , " \" one \" " ) ,
( " -Q " , " \" one \" " ) ,
( " --quote-name " , " \" one \" " ) ,
( " --quoting-style=escape " , " one " ) ,
( " -b " , " one " ) ,
( " --quoting-style=shell-escape " , " one " ) ,
( " --quoting-style=shell-escape-always " , " 'one' " ) ,
( " --quoting-style=shell " , " one " ) ,
( " --quoting-style=shell-always " , " 'one' " ) ,
] {
2021-04-07 09:48:01 +00:00
scene
. ucmd ( )
2021-08-31 10:55:17 +00:00
. arg ( " --hide-control-chars " )
2021-04-07 09:48:01 +00:00
. arg ( arg )
. arg ( " one " )
. succeeds ( )
2023-01-27 09:29:45 +00:00
. stdout_only ( format! ( " {correct} \n " ) ) ;
2021-04-01 20:50:13 +00:00
}
}
2021-04-04 17:19:56 +00:00
2022-06-02 21:44:18 +00:00
#[ test ]
fn test_ls_quoting_and_color ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " one two " ) ;
scene
. ucmd ( )
. arg ( " --color " )
. arg ( " one two " )
. succeeds ( )
2023-12-10 15:07:41 +00:00
. stdout_only ( " one two \n " ) ;
// TODO: TTY-expected output
// .stdout_only("'one two'\n");
2022-06-02 21:44:18 +00:00
}
2021-04-04 17:19:56 +00:00
#[ test ]
fn test_ls_ignore_hide ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " README.md " ) ;
at . touch ( " CONTRIBUTING.md " ) ;
at . touch ( " some_other_file " ) ;
at . touch ( " READMECAREFULLY.md " ) ;
2021-04-04 21:14:55 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --hide=* " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --ignore=* " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --ignore=irrelevant pattern " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " CONTRIBUTING.md \n README.md \n READMECAREFULLY.md \n some_other_file \n " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --ignore=README*.md " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " CONTRIBUTING.md \n some_other_file \n " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --hide=README*.md " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " CONTRIBUTING.md \n some_other_file \n " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --ignore=*.md " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " some_other_file \n " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
. arg ( " -a " )
2022-01-18 12:47:50 +00:00
. arg ( " --ignore=*.md " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " . \n .. \n some_other_file \n " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
. arg ( " -a " )
2022-01-18 12:47:50 +00:00
. arg ( " --hide=*.md " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " . \n .. \n CONTRIBUTING.md \n README.md \n READMECAREFULLY.md \n some_other_file \n " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
. arg ( " -A " )
2022-01-18 12:47:50 +00:00
. arg ( " --ignore=*.md " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " some_other_file \n " ) ;
2021-04-04 17:19:56 +00:00
scene
. ucmd ( )
. arg ( " -A " )
2022-01-18 12:47:50 +00:00
. arg ( " --hide=*.md " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 17:19:56 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " CONTRIBUTING.md \n README.md \n READMECAREFULLY.md \n some_other_file \n " ) ;
2021-04-04 20:35:22 +00:00
// Stacking multiple patterns
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --ignore=README* " )
. arg ( " --ignore=CONTRIBUTING* " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 20:35:22 +00:00
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " some_other_file \n " ) ;
2021-04-04 20:35:22 +00:00
2021-04-05 10:17:42 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --hide=README* " )
. arg ( " --ignore=CONTRIBUTING* " )
2021-04-05 10:17:42 +00:00
. arg ( " -1 " )
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " some_other_file \n " ) ;
2021-04-05 10:17:42 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --hide=README* " )
. arg ( " --hide=CONTRIBUTING* " )
2021-04-05 10:17:42 +00:00
. arg ( " -1 " )
. succeeds ( )
2021-04-07 09:48:01 +00:00
. stdout_only ( " some_other_file \n " ) ;
2021-04-05 10:17:42 +00:00
2021-04-04 20:35:22 +00:00
// Invalid patterns
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --ignore=READ[ME " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 20:35:22 +00:00
. succeeds ( )
2022-10-12 20:04:21 +00:00
. stderr_contains ( " Invalid pattern " )
2021-04-07 09:48:01 +00:00
. stdout_is ( " CONTRIBUTING.md \n README.md \n READMECAREFULLY.md \n some_other_file \n " ) ;
2021-04-04 20:35:22 +00:00
scene
. ucmd ( )
2022-01-18 12:47:50 +00:00
. arg ( " --hide=READ[ME " )
2021-04-04 21:14:55 +00:00
. arg ( " -1 " )
2021-04-04 20:35:22 +00:00
. succeeds ( )
2022-10-12 20:04:21 +00:00
. stderr_contains ( " Invalid pattern " )
2021-04-07 09:48:01 +00:00
. stdout_is ( " CONTRIBUTING.md \n README.md \n READMECAREFULLY.md \n some_other_file \n " ) ;
2021-04-04 17:19:56 +00:00
}
2021-04-14 12:12:00 +00:00
2021-04-21 10:03:48 +00:00
#[ test ]
2021-12-22 17:31:45 +00:00
#[ cfg(unix) ]
2021-04-21 10:03:48 +00:00
fn test_ls_ignore_backups ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " somefile " ) ;
at . touch ( " somebackup~ " ) ;
at . touch ( " .somehiddenfile " ) ;
at . touch ( " .somehiddenbackup~ " ) ;
scene . ucmd ( ) . arg ( " -B " ) . succeeds ( ) . stdout_is ( " somefile \n " ) ;
scene
. ucmd ( )
. arg ( " --ignore-backups " )
. succeeds ( )
. stdout_is ( " somefile \n " ) ;
scene
. ucmd ( )
. arg ( " -aB " )
. succeeds ( )
. stdout_contains ( " .somehiddenfile " )
. stdout_contains ( " somefile " )
. stdout_does_not_contain ( " somebackup " )
. stdout_does_not_contain ( " .somehiddenbackup~ " ) ;
scene
. ucmd ( )
. arg ( " -a " )
. arg ( " --ignore-backups " )
. succeeds ( )
. stdout_contains ( " .somehiddenfile " )
. stdout_contains ( " somefile " )
. stdout_does_not_contain ( " somebackup " )
. stdout_does_not_contain ( " .somehiddenbackup~ " ) ;
}
2021-04-21 10:06:54 +00:00
2022-07-31 16:46:11 +00:00
// This test fails on windows, see details at #3985
#[ cfg(not(windows)) ]
#[ test ]
fn test_ls_ignore_explicit_period ( ) {
// In ls ignore patterns, leading periods must be explicitly specified
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " .hidden.yml " ) ;
at . touch ( " regular.yml " ) ;
scene
. ucmd ( )
. arg ( " -a " )
. arg ( " --ignore " )
. arg ( " ?hidden.yml " )
. succeeds ( )
. stdout_contains ( " .hidden.yml " )
. stdout_contains ( " regular.yml " ) ;
scene
. ucmd ( )
. arg ( " -a " )
. arg ( " --ignore " )
. arg ( " *.yml " )
. succeeds ( )
. stdout_contains ( " .hidden.yml " )
. stdout_does_not_contain ( " regular.yml " ) ;
// Leading period is explicitly specified
scene
. ucmd ( )
. arg ( " -a " )
. arg ( " --ignore " )
. arg ( " .*.yml " )
. succeeds ( )
. stdout_does_not_contain ( " .hidden.yml " )
. stdout_contains ( " regular.yml " ) ;
}
// This test fails on windows, see details at #3985
#[ cfg(not(windows)) ]
#[ test ]
fn test_ls_ignore_negation ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " apple " ) ;
at . touch ( " boy " ) ;
scene
. ucmd ( )
. arg ( " --ignore " )
. arg ( " [!a]* " )
. succeeds ( )
. stdout_contains ( " apple " )
. stdout_does_not_contain ( " boy " ) ;
scene
. ucmd ( )
. arg ( " --ignore " )
. arg ( " [^a]* " )
. succeeds ( )
. stdout_contains ( " apple " )
. stdout_does_not_contain ( " boy " ) ;
}
2021-04-14 12:12:00 +00:00
#[ test ]
fn test_ls_directory ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " some_dir " ) ;
at . symlink_dir ( " some_dir " , " sym_dir " ) ;
2021-04-16 16:27:36 +00:00
at . touch ( Path ::new ( " some_dir " ) . join ( " nested_file " ) . to_str ( ) . unwrap ( ) ) ;
2021-04-14 12:12:00 +00:00
scene
. ucmd ( )
. arg ( " some_dir " )
. succeeds ( )
. stdout_is ( " nested_file \n " ) ;
scene
. ucmd ( )
. arg ( " --directory " )
. arg ( " some_dir " )
. succeeds ( )
. stdout_is ( " some_dir \n " ) ;
scene
. ucmd ( )
. arg ( " sym_dir " )
. succeeds ( )
. stdout_is ( " nested_file \n " ) ;
}
#[ test ]
fn test_ls_deref_command_line ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " some_file " ) ;
at . symlink_file ( " some_file " , " sym_file " ) ;
2021-04-14 12:42:14 +00:00
scene
. ucmd ( )
. arg ( " sym_file " )
. succeeds ( )
. stdout_is ( " sym_file \n " ) ;
// -l changes the default to no dereferencing
2021-04-14 12:12:00 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " sym_file " )
. succeeds ( )
. stdout_contains ( " sym_file -> " ) ;
2021-04-14 12:42:14 +00:00
scene
. ucmd ( )
. arg ( " --dereference-command-line-symlink-to-dir " )
. arg ( " sym_file " )
. succeeds ( )
. stdout_is ( " sym_file \n " ) ;
2021-04-14 12:12:00 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --dereference-command-line-symlink-to-dir " )
. arg ( " sym_file " )
. succeeds ( )
. stdout_contains ( " sym_file -> " ) ;
2021-04-14 12:42:14 +00:00
scene
. ucmd ( )
. arg ( " --dereference-command-line " )
. arg ( " sym_file " )
. succeeds ( )
. stdout_is ( " sym_file \n " ) ;
2021-04-14 12:12:00 +00:00
let result = scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --dereference-command-line " )
. arg ( " sym_file " )
. succeeds ( ) ;
assert! ( ! result . stdout_str ( ) . contains ( " -> " ) ) ;
let result = scene . ucmd ( ) . arg ( " -lH " ) . arg ( " sym_file " ) . succeeds ( ) ;
assert! ( ! result . stdout_str ( ) . contains ( " sym_file -> " ) ) ;
// If the symlink is not a command line argument, it must be shown normally
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --dereference-command-line " )
. succeeds ( )
. stdout_contains ( " sym_file -> " ) ;
}
#[ test ]
fn test_ls_deref_command_line_dir ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " some_dir " ) ;
at . symlink_dir ( " some_dir " , " sym_dir " ) ;
2021-04-16 16:27:36 +00:00
at . touch ( Path ::new ( " some_dir " ) . join ( " nested_file " ) . to_str ( ) . unwrap ( ) ) ;
2021-04-14 12:12:00 +00:00
2021-04-14 12:42:14 +00:00
scene
. ucmd ( )
. arg ( " sym_dir " )
. succeeds ( )
. stdout_contains ( " nested_file " ) ;
2021-04-14 12:12:00 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " sym_dir " )
. succeeds ( )
2021-04-14 12:42:14 +00:00
. stdout_contains ( " sym_dir -> " ) ;
scene
. ucmd ( )
. arg ( " --dereference-command-line-symlink-to-dir " )
. arg ( " sym_dir " )
. succeeds ( )
2021-04-14 12:12:00 +00:00
. stdout_contains ( " nested_file " ) ;
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --dereference-command-line-symlink-to-dir " )
. arg ( " sym_dir " )
. succeeds ( )
. stdout_contains ( " nested_file " ) ;
2021-04-14 12:42:14 +00:00
scene
. ucmd ( )
. arg ( " --dereference-command-line " )
. arg ( " sym_dir " )
. succeeds ( )
. stdout_contains ( " nested_file " ) ;
2021-04-14 12:12:00 +00:00
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --dereference-command-line " )
. arg ( " sym_dir " )
. succeeds ( )
. stdout_contains ( " nested_file " ) ;
scene
. ucmd ( )
. arg ( " -lH " )
. arg ( " sym_dir " )
. succeeds ( )
. stdout_contains ( " nested_file " ) ;
// If the symlink is not a command line argument, it must be shown normally
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --dereference-command-line " )
. succeeds ( )
. stdout_contains ( " sym_dir -> " ) ;
scene
. ucmd ( )
. arg ( " -lH " )
. succeeds ( )
. stdout_contains ( " sym_dir -> " ) ;
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --dereference-command-line-symlink-to-dir " )
. succeeds ( )
. stdout_contains ( " sym_dir -> " ) ;
// --directory does not dereference anything by default
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --directory " )
. arg ( " sym_dir " )
. succeeds ( )
. stdout_contains ( " sym_dir -> " ) ;
let result = scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --directory " )
. arg ( " --dereference-command-line-symlink-to-dir " )
. arg ( " sym_dir " )
. succeeds ( ) ;
assert! ( ! result . stdout_str ( ) . ends_with ( " sym_dir " ) ) ;
2021-04-14 12:42:14 +00:00
// --classify does not dereference anything by default
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --directory " )
. arg ( " sym_dir " )
. succeeds ( )
. stdout_contains ( " sym_dir -> " ) ;
let result = scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --directory " )
. arg ( " --dereference-command-line-symlink-to-dir " )
. arg ( " sym_dir " )
. succeeds ( ) ;
assert! ( ! result . stdout_str ( ) . ends_with ( " sym_dir " ) ) ;
2021-04-14 12:12:00 +00:00
}
2021-03-29 20:26:55 +00:00
#[ test ]
fn test_ls_sort_extension ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
2022-04-02 08:47:37 +00:00
for filename in [
2021-03-29 20:26:55 +00:00
" file1 " ,
" file2 " ,
" anotherFile " ,
" .hidden " ,
" .file.1 " ,
" .file.2 " ,
" file.1 " ,
" file.2 " ,
" anotherFile.1 " ,
" anotherFile.2 " ,
" file.ext " ,
" file.debug " ,
" anotherFile.ext " ,
" anotherFile.debug " ,
] {
at . touch ( filename ) ;
}
let expected = vec! [
" . " ,
" .. " ,
2021-04-26 12:42:42 +00:00
" .hidden " ,
2021-03-29 20:26:55 +00:00
" anotherFile " ,
" file1 " ,
" file2 " ,
" .file.1 " ,
" anotherFile.1 " ,
" file.1 " ,
" .file.2 " ,
" anotherFile.2 " ,
" file.2 " ,
" anotherFile.debug " ,
" file.debug " ,
" anotherFile.ext " ,
" file.ext " ,
" " , // because of '\n' at the end of the output
] ;
let result = scene . ucmd ( ) . arg ( " -1aX " ) . run ( ) ;
2021-04-26 12:42:42 +00:00
assert_eq! (
result . stdout_str ( ) . split ( '\n' ) . collect ::< Vec < _ > > ( ) ,
expected ,
) ;
2021-03-29 20:26:55 +00:00
let result = scene . ucmd ( ) . arg ( " -1a " ) . arg ( " --sort=extension " ) . run ( ) ;
2021-04-26 12:42:42 +00:00
assert_eq! (
result . stdout_str ( ) . split ( '\n' ) . collect ::< Vec < _ > > ( ) ,
expected ,
) ;
2021-04-25 21:33:19 +00:00
}
2021-05-18 00:00:16 +00:00
#[ test ]
fn test_ls_path ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
let file1 = " file1 " ;
let file2 = " file2 " ;
let dir = " dir " ;
2023-01-27 09:29:45 +00:00
let path = & format! ( " {dir} / {file2} " ) ;
2021-05-18 00:00:16 +00:00
at . mkdir ( dir ) ;
at . touch ( file1 ) ;
at . touch ( path ) ;
2023-01-27 09:29:45 +00:00
let expected_stdout = & format! ( " {path} \n " ) ;
2021-05-18 00:00:16 +00:00
scene . ucmd ( ) . arg ( path ) . run ( ) . stdout_is ( expected_stdout ) ;
2023-01-27 09:29:45 +00:00
let expected_stdout = & format! ( " ./ {path} \n " ) ;
2021-05-21 16:50:54 +00:00
scene
. ucmd ( )
2023-01-27 09:29:45 +00:00
. arg ( format! ( " ./ {path} " ) )
2021-05-21 16:50:54 +00:00
. run ( )
. stdout_is ( expected_stdout ) ;
2021-05-18 00:00:16 +00:00
2021-05-21 16:50:54 +00:00
let abs_path = format! ( " {} / {} " , at . as_string ( ) , path ) ;
2023-12-10 15:07:41 +00:00
let expected_stdout = format! ( " {abs_path} \n " ) ;
2021-05-21 16:50:54 +00:00
scene . ucmd ( ) . arg ( & abs_path ) . run ( ) . stdout_is ( expected_stdout ) ;
2021-05-18 00:00:16 +00:00
2023-01-27 09:29:45 +00:00
let expected_stdout = format! ( " {path} \n {file1} \n " ) ;
2021-05-18 00:00:16 +00:00
scene
. ucmd ( )
. arg ( file1 )
. arg ( path )
. run ( )
. stdout_is ( expected_stdout ) ;
}
2021-06-20 08:20:38 +00:00
#[ test ]
fn test_ls_dangling_symlinks ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " temp_dir " ) ;
at . symlink_file ( " does_not_exist " , " temp_dir/dangle " ) ;
2022-07-10 20:12:59 +00:00
scene
. ucmd ( )
. arg ( " -L " )
. arg ( " temp_dir/dangle " )
. fails ( )
. code_is ( 2 ) ;
scene
. ucmd ( )
. arg ( " -H " )
. arg ( " temp_dir/dangle " )
. fails ( )
. code_is ( 2 ) ;
2021-06-20 08:20:38 +00:00
scene
. ucmd ( )
. arg ( " temp_dir/dangle " )
. succeeds ( )
. stdout_contains ( " dangle " ) ;
scene
. ucmd ( )
. arg ( " -Li " )
. arg ( " temp_dir " )
2022-01-05 13:50:37 +00:00
. fails ( )
2022-07-10 20:12:59 +00:00
. code_is ( 1 )
2022-01-05 13:50:37 +00:00
. stderr_contains ( " cannot access " )
2022-03-15 15:27:43 +00:00
. stderr_contains ( " No such file or directory " )
. stdout_contains ( if cfg! ( windows ) { " dangle " } else { " ? dangle " } ) ;
2022-01-05 13:50:37 +00:00
2023-06-08 09:26:21 +00:00
scene
. ucmd ( )
. arg ( " -LZ " )
. arg ( " temp_dir " )
. fails ( )
. code_is ( 1 )
. stderr_contains ( " cannot access " )
. stderr_contains ( " No such file or directory " )
. stdout_contains ( if cfg! ( windows ) { " dangle " } else { " ? dangle " } ) ;
2022-01-05 13:50:37 +00:00
scene
. ucmd ( )
. arg ( " -Ll " )
. arg ( " temp_dir " )
. fails ( )
2022-07-10 20:12:59 +00:00
. code_is ( 1 )
2022-01-05 13:50:37 +00:00
. stdout_contains ( " l????????? " ) ;
2022-01-11 11:01:54 +00:00
#[ cfg(unix) ]
{
// Check padding is the same for real files and dangling links, in non-long formats
at . touch ( " temp_dir/real_file " ) ;
let real_file_res = scene . ucmd ( ) . arg ( " -Li1 " ) . arg ( " temp_dir " ) . fails ( ) ;
2022-07-10 20:12:59 +00:00
real_file_res . code_is ( 1 ) ;
2022-01-11 11:01:54 +00:00
let real_file_stdout_len = String ::from_utf8 ( real_file_res . stdout ( ) . to_owned ( ) )
. ok ( )
. unwrap ( )
. lines ( )
. nth ( 1 )
. unwrap ( )
. strip_suffix ( " real_file " )
. unwrap ( )
. len ( ) ;
let dangle_file_res = scene . ucmd ( ) . arg ( " -Li1 " ) . arg ( " temp_dir " ) . fails ( ) ;
2022-07-10 20:12:59 +00:00
dangle_file_res . code_is ( 1 ) ;
2022-01-11 11:01:54 +00:00
let dangle_stdout_len = String ::from_utf8 ( dangle_file_res . stdout ( ) . to_owned ( ) )
. ok ( )
. unwrap ( )
. lines ( )
. next ( )
. unwrap ( )
. strip_suffix ( " dangle " )
. unwrap ( )
. len ( ) ;
assert_eq! ( real_file_stdout_len , dangle_stdout_len ) ;
}
2021-06-20 08:20:38 +00:00
}
2021-09-10 19:35:00 +00:00
#[ test ]
#[ cfg(feature = " feat_selinux " ) ]
2021-09-13 11:24:44 +00:00
fn test_ls_context1 ( ) {
use selinux ::{ self , KernelSupport } ;
if selinux ::kernel_support ( ) = = KernelSupport ::Unsupported {
println! ( " test skipped: Kernel has no support for SElinux context " , ) ;
return ;
}
let file = " test_ls_context_file " ;
let expected = format! ( " unconfined_u:object_r:user_tmp_t:s0 {} \n " , file ) ;
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . touch ( file ) ;
ucmd . args ( & [ " -Z " , file ] ) . succeeds ( ) . stdout_is ( expected ) ;
}
#[ test ]
#[ cfg(feature = " feat_selinux " ) ]
fn test_ls_context2 ( ) {
2021-09-10 19:35:00 +00:00
use selinux ::{ self , KernelSupport } ;
if selinux ::kernel_support ( ) = = KernelSupport ::Unsupported {
println! ( " test skipped: Kernel has no support for SElinux context " , ) ;
return ;
}
let ts = TestScenario ::new ( util_name! ( ) ) ;
2022-04-02 08:47:37 +00:00
for c_flag in [ " -Z " , " --context " ] {
2021-09-10 19:35:00 +00:00
ts . ucmd ( )
2022-05-14 02:47:56 +00:00
. args ( & [ c_flag , " / " ] )
2021-09-10 19:35:00 +00:00
. succeeds ( )
2022-01-21 20:56:42 +00:00
. stdout_only ( unwrap_or_return! ( expected_result ( & ts , & [ c_flag , " / " ] ) ) . stdout_str ( ) ) ;
2021-09-10 19:35:00 +00:00
}
}
#[ test ]
#[ cfg(feature = " feat_selinux " ) ]
fn test_ls_context_format ( ) {
use selinux ::{ self , KernelSupport } ;
if selinux ::kernel_support ( ) = = KernelSupport ::Unsupported {
println! ( " test skipped: Kernel has no support for SElinux context " , ) ;
return ;
}
let ts = TestScenario ::new ( util_name! ( ) ) ;
// NOTE:
// --format=long/verbose matches the output of GNU's ls for --context
// except for the size count which may differ to the size count reported by GNU's ls.
2022-04-02 08:47:37 +00:00
for word in [
2021-09-10 19:35:00 +00:00
" across " ,
" commas " ,
" horizontal " ,
// "long",
" single-column " ,
// "verbose",
" vertical " ,
] {
let format = format! ( " --format= {} " , word ) ;
ts . ucmd ( )
2022-10-23 04:18:56 +00:00
. args ( & [ " -Z " , format . as_str ( ) , " / " ] )
2021-09-10 19:35:00 +00:00
. succeeds ( )
. stdout_only (
2022-01-21 20:56:42 +00:00
unwrap_or_return! ( expected_result ( & ts , & [ " -Z " , format . as_str ( ) , " / " ] ) ) . stdout_str ( ) ,
2021-09-10 19:35:00 +00:00
) ;
}
}
2022-03-20 18:58:21 +00:00
#[ test ]
#[ allow(non_snake_case) ]
fn test_ls_a_A ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ucmd ( )
. arg ( " -A " )
. arg ( " -a " )
. succeeds ( )
. stdout_contains ( " . " )
. stdout_contains ( " .. " ) ;
scene
. ucmd ( )
. arg ( " -a " )
. arg ( " -A " )
. succeeds ( )
. stdout_does_not_contain ( " . " )
. stdout_does_not_contain ( " .. " ) ;
}
2022-03-21 12:32:23 +00:00
#[ test ]
#[ allow(non_snake_case) ]
fn test_ls_multiple_a_A ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ucmd ( )
. arg ( " -a " )
. arg ( " -a " )
. succeeds ( )
. stdout_contains ( " . " )
. stdout_contains ( " .. " ) ;
scene
. ucmd ( )
. arg ( " -A " )
. arg ( " -A " )
. succeeds ( )
. stdout_does_not_contain ( " . " )
. stdout_does_not_contain ( " .. " ) ;
}
2022-03-26 10:30:07 +00:00
#[ test ]
2022-09-29 17:59:27 +00:00
#[ cfg(feature = " ln " ) ]
2022-03-26 10:30:07 +00:00
fn test_ls_quoting ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ccmd ( " ln " )
. arg ( " -s " )
. arg ( " 'need quoting' " )
. arg ( " symlink " )
. succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --quoting-style=shell-escape " )
. arg ( " symlink " )
. succeeds ( )
. stdout_contains ( " \' need quoting \' " ) ;
}
2022-03-26 10:45:47 +00:00
#[ test ]
2022-09-29 17:59:27 +00:00
#[ cfg(feature = " ln " ) ]
2022-03-26 10:45:47 +00:00
fn test_ls_quoting_color ( ) {
2022-03-26 10:30:07 +00:00
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ccmd ( " ln " )
. arg ( " -s " )
. arg ( " 'need quoting' " )
. arg ( " symlink " )
. succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " --quoting-style=shell-escape " )
. arg ( " --color=auto " )
. arg ( " symlink " )
. succeeds ( )
. stdout_contains ( " \' need quoting \' " ) ;
}
2022-07-11 15:18:58 +00:00
#[ test ]
fn test_ls_dereference_looped_symlinks_recursive ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkdir ( " loop " ) ;
at . relative_symlink_dir ( " ../loop " , " loop/sub " ) ;
ucmd . args ( & [ " -RL " , " loop " ] )
. fails ( )
2022-07-10 20:12:59 +00:00
. code_is ( 2 )
2022-07-11 15:18:58 +00:00
. stderr_contains ( " not listing already-listed directory " ) ;
}
#[ test ]
fn test_dereference_dangling_color ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . relative_symlink_file ( " wat " , " nonexistent " ) ;
let out_exp = ucmd . args ( & [ " --color " ] ) . run ( ) . stdout_move_str ( ) ;
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . relative_symlink_file ( " wat " , " nonexistent " ) ;
ucmd . args ( & [ " -L " , " --color " ] )
. fails ( )
. code_is ( 1 )
. stderr_contains ( " No such file or directory " )
. stdout_is ( out_exp ) ;
}
#[ test ]
fn test_dereference_symlink_dir_color ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkdir ( " dir1 " ) ;
at . mkdir ( " dir1/link " ) ;
let out_exp = ucmd . args ( & [ " --color " , " dir1 " ] ) . run ( ) . stdout_move_str ( ) ;
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkdir ( " dir1 " ) ;
at . mkdir ( " dir2 " ) ;
at . relative_symlink_dir ( " ../dir2 " , " dir1/link " ) ;
ucmd . args ( & [ " -L " , " --color " , " dir1 " ] )
. succeeds ( )
. stdout_is ( out_exp ) ;
}
#[ test ]
fn test_dereference_symlink_file_color ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkdir ( " dir1 " ) ;
at . touch ( " dir1/link " ) ;
let out_exp = ucmd . args ( & [ " --color " , " dir1 " ] ) . run ( ) . stdout_move_str ( ) ;
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkdir ( " dir1 " ) ;
at . touch ( " file " ) ;
at . relative_symlink_file ( " ../file " , " dir1/link " ) ;
ucmd . args ( & [ " -L " , " --color " , " dir1 " ] )
. succeeds ( )
. stdout_is ( out_exp ) ;
}
2022-07-26 08:35:43 +00:00
#[ test ]
fn test_tabsize_option ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene . ucmd ( ) . args ( & [ " -T " , " 3 " ] ) . succeeds ( ) ;
scene . ucmd ( ) . args ( & [ " --tabsize " , " 0 " ] ) . succeeds ( ) ;
scene . ucmd ( ) . arg ( " -T " ) . fails ( ) ;
}
#[ ignore = " issue #3624 " ]
#[ test ]
fn test_tabsize_formatting ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . touch ( " aaaaaaaa " ) ;
at . touch ( " bbbb " ) ;
at . touch ( " cccc " ) ;
at . touch ( " dddddddd " ) ;
ucmd . args ( & [ " -T " , " 4 " ] )
. succeeds ( )
. stdout_is ( " aaaaaaaa bbbb \n cccc \t dddddddd " ) ;
ucmd . args ( & [ " -T " , " 2 " ] )
. succeeds ( )
. stdout_is ( " aaaaaaaa bbbb \n cccc \t \t dddddddd " ) ;
// use spaces
ucmd . args ( & [ " -T " , " 0 " ] )
. succeeds ( )
. stdout_is ( " aaaaaaaa bbbb \n cccc dddddddd " ) ;
}
2023-05-11 10:58:36 +00:00
#[ cfg(any(
target_os = " linux " ,
target_os = " macos " ,
target_os = " ios " ,
target_os = " freebsd " ,
target_os = " dragonfly " ,
target_os = " netbsd " ,
target_os = " openbsd " ,
target_os = " illumos " ,
target_os = " solaris "
) ) ]
#[ test ]
fn test_device_number ( ) {
use std ::fs ::{ metadata , read_dir } ;
use std ::os ::unix ::fs ::{ FileTypeExt , MetadataExt } ;
use uucore ::libc ::{ dev_t , major , minor } ;
let dev_dir = read_dir ( " /dev " ) . unwrap ( ) ;
// let's use the first device for test
let blk_dev = dev_dir
. map ( | res_entry | res_entry . unwrap ( ) )
. find ( | entry | {
entry . file_type ( ) . unwrap ( ) . is_block_device ( )
| | entry . file_type ( ) . unwrap ( ) . is_char_device ( )
} )
. expect ( " Expect a block/char device " ) ;
let blk_dev_path = blk_dev . path ( ) ;
let blk_dev_meta = metadata ( blk_dev_path . as_path ( ) ) . unwrap ( ) ;
let blk_dev_number = blk_dev_meta . rdev ( ) as dev_t ;
let ( major , minor ) = unsafe { ( major ( blk_dev_number ) , minor ( blk_dev_number ) ) } ;
let major_minor_str = format! ( " {} , {} " , major , minor ) ;
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ucmd ( )
. arg ( " -l " )
. arg ( blk_dev_path . to_str ( ) . expect ( " should be UTF-8 encoded " ) )
. succeeds ( )
. stdout_contains ( major_minor_str ) ;
}
2023-05-14 19:47:58 +00:00
#[ test ]
#[ cfg(target_os = " linux " ) ]
fn test_invalid_utf8 ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
let filename = OsStr ::from_bytes ( b " - \xE0 -foo " ) ;
at . touch ( filename ) ;
ucmd . succeeds ( ) ;
}
2023-09-15 20:34:17 +00:00
#[ cfg(all(unix, feature = " chmod " )) ]
#[ test ]
fn test_ls_perm_io_errors ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " d " ) ;
at . symlink_file ( " / " , " d/s " ) ;
scene . ccmd ( " chmod " ) . arg ( " 600 " ) . arg ( " d " ) . succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -l " )
. arg ( " d " )
. fails ( )
. code_is ( 1 )
. stderr_contains ( " Permission denied " ) ;
}
2023-09-20 06:17:46 +00:00
#[ test ]
fn test_ls_dired_incompatible ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ucmd ( )
. arg ( " --dired " )
. fails ( )
. code_is ( 1 )
. stderr_contains ( " --dired requires --format=long " ) ;
}
2023-10-24 12:48:24 +00:00
#[ test ]
fn test_ls_dired_and_zero_are_incompatible ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ucmd ( )
. arg ( " --dired " )
. arg ( " -l " )
. arg ( " --zero " )
. fails ( )
. code_is ( 2 )
. stderr_contains ( " --dired and --zero are incompatible " ) ;
}
2023-09-20 06:17:46 +00:00
#[ test ]
fn test_ls_dired_recursive ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ucmd ( )
. arg ( " --dired " )
. arg ( " -l " )
. arg ( " -R " )
. succeeds ( )
. stdout_does_not_contain ( " //DIRED// " )
. stdout_contains ( " total 0 " )
. stdout_contains ( " //SUBDIRED// 2 3 " )
. stdout_contains ( " //DIRED-OPTIONS// --quoting-style " ) ;
}
2023-10-19 12:17:34 +00:00
#[ test ]
fn test_ls_dired_recursive_multiple ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " d " ) ;
at . mkdir ( " d/d1 " ) ;
at . mkdir ( " d/d2 " ) ;
at . touch ( " d/d2/a " ) ;
at . touch ( " d/d2/c2 " ) ;
at . touch ( " d/d1/f1 " ) ;
at . touch ( " d/d1/file-long " ) ;
let mut cmd = scene . ucmd ( ) ;
cmd . arg ( " --dired " ) . arg ( " -l " ) . arg ( " -R " ) . arg ( " d " ) ;
let result = cmd . succeeds ( ) ;
let output = result . stdout_str ( ) . to_string ( ) ;
println! ( " Output: \n {} " , output ) ;
let dired_line = output
. lines ( )
. find ( | & line | line . starts_with ( " //DIRED// " ) )
. unwrap ( ) ;
let positions : Vec < usize > = dired_line
. split_whitespace ( )
. skip ( 1 )
. map ( | s | s . parse ( ) . unwrap ( ) )
. collect ( ) ;
println! ( " Parsed byte positions: {:?} " , positions ) ;
assert_eq! ( positions . len ( ) % 2 , 0 ) ; // Ensure there's an even number of positions
let filenames : Vec < String > = positions
. chunks ( 2 )
. map ( | chunk | {
let start_pos = chunk [ 0 ] ;
let end_pos = chunk [ 1 ] ;
let filename = String ::from_utf8 ( output . as_bytes ( ) [ start_pos ..= end_pos ] . to_vec ( ) )
. unwrap ( )
. trim ( )
. to_string ( ) ;
println! ( " Extracted filename: {} " , filename ) ;
filename
} )
. collect ( ) ;
println! ( " Extracted filenames: {:?} " , filenames ) ;
assert_eq! ( filenames , vec! [ " d1 " , " d2 " , " f1 " , " file-long " , " a " , " c2 " ] ) ;
}
2023-09-20 06:17:46 +00:00
#[ test ]
fn test_ls_dired_simple ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
scene
. ucmd ( )
. arg ( " --dired " )
. arg ( " -l " )
. succeeds ( )
. stdout_contains ( " total 0 " ) ;
at . mkdir ( " d " ) ;
at . touch ( " d/a1 " ) ;
let mut cmd = scene . ucmd ( ) ;
cmd . arg ( " --dired " ) . arg ( " -l " ) . arg ( " d " ) ;
let result = cmd . succeeds ( ) ;
result . stdout_contains ( " total 0 " ) ;
println! ( " result.stdout = {:#?} " , result . stdout_str ( ) ) ;
let dired_line = result
. stdout_str ( )
. lines ( )
. find ( | & line | line . starts_with ( " //DIRED// " ) )
. unwrap ( ) ;
let positions : Vec < usize > = dired_line
. split_whitespace ( )
. skip ( 1 )
. map ( | s | s . parse ( ) . unwrap ( ) )
. collect ( ) ;
assert_eq! ( positions . len ( ) , 2 ) ;
let start_pos = positions [ 0 ] ;
let end_pos = positions [ 1 ] ;
// Extract the filename using the positions
let filename =
String ::from_utf8 ( result . stdout_str ( ) . as_bytes ( ) [ start_pos .. end_pos ] . to_vec ( ) ) . unwrap ( ) ;
assert_eq! ( filename , " a1 " ) ;
}
#[ test ]
fn test_ls_dired_complex ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " d " ) ;
at . mkdir ( " d/d " ) ;
at . touch ( " d/a1 " ) ;
at . touch ( " d/a22 " ) ;
at . touch ( " d/a333 " ) ;
at . touch ( " d/a4444 " ) ;
let mut cmd = scene . ucmd ( ) ;
cmd . arg ( " --dired " ) . arg ( " -l " ) . arg ( " d " ) ;
let result = cmd . succeeds ( ) ;
2023-09-22 08:23:01 +00:00
// Number of blocks. We run this test only if the default size of a newly created directory is
// 4096 bytes to prevent it from failing where this is not the case (e.g. using tmpfs for /tmp).
2023-09-20 06:17:46 +00:00
#[ cfg(target_os = " linux " ) ]
2023-09-22 08:23:01 +00:00
if at . metadata ( " d/d " ) . len ( ) = = 4096 {
result . stdout_contains ( " total 4 " ) ;
}
2023-09-20 06:17:46 +00:00
let output = result . stdout_str ( ) . to_string ( ) ;
println! ( " Output: \n {} " , output ) ;
let dired_line = output
. lines ( )
. find ( | & line | line . starts_with ( " //DIRED// " ) )
. unwrap ( ) ;
let positions : Vec < usize > = dired_line
. split_whitespace ( )
. skip ( 1 )
. map ( | s | s . parse ( ) . unwrap ( ) )
. collect ( ) ;
println! ( " {:?} " , positions ) ;
println! ( " Parsed byte positions: {:?} " , positions ) ;
assert_eq! ( positions . len ( ) % 2 , 0 ) ; // Ensure there's an even number of positions
let filenames : Vec < String > = positions
. chunks ( 2 )
. map ( | chunk | {
let start_pos = chunk [ 0 ] ;
let end_pos = chunk [ 1 ] ;
let filename = String ::from_utf8 ( output . as_bytes ( ) [ start_pos ..= end_pos ] . to_vec ( ) )
. unwrap ( )
. trim ( )
. to_string ( ) ;
println! ( " Extracted filename: {} " , filename ) ;
filename
} )
. collect ( ) ;
println! ( " Extracted filenames: {:?} " , filenames ) ;
assert_eq! ( filenames , vec! [ " a1 " , " a22 " , " a333 " , " a4444 " , " d " ] ) ;
}
2023-10-12 21:07:27 +00:00
2023-10-19 12:17:34 +00:00
#[ test ]
fn test_ls_subdired_complex ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " dir1 " ) ;
at . mkdir ( " dir1/d " ) ;
at . mkdir ( " dir1/c2 " ) ;
at . touch ( " dir1/a1 " ) ;
at . touch ( " dir1/a22 " ) ;
at . touch ( " dir1/a333 " ) ;
at . touch ( " dir1/c2/a4444 " ) ;
let mut cmd = scene . ucmd ( ) ;
cmd . arg ( " --dired " ) . arg ( " -l " ) . arg ( " -R " ) . arg ( " dir1 " ) ;
let result = cmd . succeeds ( ) ;
let output = result . stdout_str ( ) . to_string ( ) ;
println! ( " Output: \n {} " , output ) ;
let dired_line = output
. lines ( )
. find ( | & line | line . starts_with ( " //SUBDIRED// " ) )
. unwrap ( ) ;
let positions : Vec < usize > = dired_line
. split_whitespace ( )
. skip ( 1 )
. map ( | s | s . parse ( ) . unwrap ( ) )
. collect ( ) ;
println! ( " Parsed byte positions: {:?} " , positions ) ;
assert_eq! ( positions . len ( ) % 2 , 0 ) ; // Ensure there's an even number of positions
let dirnames : Vec < String > = positions
. chunks ( 2 )
. map ( | chunk | {
let start_pos = chunk [ 0 ] ;
let end_pos = chunk [ 1 ] ;
let dirname =
String ::from_utf8 ( output . as_bytes ( ) [ start_pos .. end_pos ] . to_vec ( ) ) . unwrap ( ) ;
println! ( " Extracted dirname: {} " , dirname ) ;
dirname
} )
. collect ( ) ;
println! ( " Extracted dirnames: {:?} " , dirnames ) ;
#[ cfg(unix) ]
assert_eq! ( dirnames , vec! [ " dir1 " , " dir1/c2 " , " dir1/d " ] ) ;
#[ cfg(windows) ]
assert_eq! ( dirnames , vec! [ " dir1 " , " dir1 \\ c2 " , " dir1 \\ d " ] ) ;
}
2023-10-12 21:07:27 +00:00
#[ ignore = " issue #5396 " ]
#[ test ]
2023-10-13 09:35:45 +00:00
fn test_ls_cf_output_should_be_delimited_by_tab ( ) {
2023-10-12 21:07:27 +00:00
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkdir ( " e " ) ;
at . mkdir ( " e/a2345 " ) ;
at . mkdir ( " e/b " ) ;
ucmd . args ( & [ " -CF " , " e " ] )
. succeeds ( )
. stdout_is ( " a2345/ \t b/ \n " ) ;
}
2023-12-01 15:27:05 +00:00
#[ cfg(all(unix, feature = " dd " )) ]
#[ test ]
fn test_posixly_correct ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ccmd ( " dd " )
. arg ( " if=/dev/zero " )
. arg ( " of=file " )
. arg ( " bs=1024 " )
. arg ( " count=1 " )
. succeeds ( ) ;
scene
. ucmd ( )
. arg ( " -s " )
. succeeds ( )
. stdout_contains_line ( " total 4 " ) ;
scene
. ucmd ( )
. arg ( " -s " )
. env ( " POSIXLY_CORRECT " , " some_value " )
. succeeds ( )
. stdout_contains_line ( " total 8 " ) ;
}
2023-12-06 14:35:38 +00:00
2023-12-08 14:10:48 +00:00
#[ test ]
fn test_ls_invalid_block_size ( ) {
new_ucmd! ( )
. arg ( " --block-size=invalid " )
. fails ( )
. code_is ( 2 )
. no_stdout ( )
. stderr_is ( " ls: invalid --block-size argument 'invalid' \n " ) ;
}
2023-12-11 15:38:55 +00:00
#[ cfg(all(unix, feature = " dd " )) ]
#[ test ]
fn test_ls_block_size_override ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
scene
. ccmd ( " dd " )
. arg ( " if=/dev/zero " )
. arg ( " of=file " )
. arg ( " bs=1024 " )
. arg ( " count=1 " )
. succeeds ( ) ;
// --si "wins"
scene
. ucmd ( )
. arg ( " -s " )
. arg ( " --block-size=512 " )
. arg ( " --si " )
. succeeds ( )
. stdout_contains_line ( " total 4.1k " ) ;
// --block-size "wins"
scene
. ucmd ( )
. arg ( " -s " )
. arg ( " --si " )
. arg ( " --block-size=512 " )
. succeeds ( )
. stdout_contains_line ( " total 8 " ) ;
// --human-readable "wins"
scene
. ucmd ( )
. arg ( " -s " )
. arg ( " --block-size=512 " )
. arg ( " --human-readable " )
. succeeds ( )
. stdout_contains_line ( " total 4.0K " ) ;
// --block-size "wins"
scene
. ucmd ( )
. arg ( " -s " )
. arg ( " --human-readable " )
. arg ( " --block-size=512 " )
. succeeds ( )
. stdout_contains_line ( " total 8 " ) ;
}
2023-12-12 13:23:51 +00:00
#[ test ]
fn test_ls_block_size_override_self ( ) {
new_ucmd! ( )
. arg ( " --block-size=512 " )
. arg ( " --block-size=512 " )
. succeeds ( ) ;
new_ucmd! ( )
. arg ( " --human-readable " )
. arg ( " --human-readable " )
. succeeds ( ) ;
new_ucmd! ( ) . arg ( " --si " ) . arg ( " --si " ) . succeeds ( ) ;
}
2023-12-06 14:35:38 +00:00
#[ test ]
fn test_ls_hyperlink ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
let file = " a.txt " ;
at . touch ( file ) ;
let path = at . root_dir_resolved ( ) ;
let separator = std ::path ::MAIN_SEPARATOR_STR ;
let result = scene . ucmd ( ) . arg ( " --hyperlink " ) . succeeds ( ) ;
assert! ( result . stdout_str ( ) . contains ( " \x1b ]8;;file:// " ) ) ;
assert! ( result
. stdout_str ( )
. contains ( & format! ( " {path} {separator} {file} \x07 {file} \x1b ]8;; \x07 " ) ) ) ;
let result = scene . ucmd ( ) . arg ( " --hyperlink=always " ) . succeeds ( ) ;
assert! ( result . stdout_str ( ) . contains ( " \x1b ]8;;file:// " ) ) ;
assert! ( result
. stdout_str ( )
. contains ( & format! ( " {path} {separator} {file} \x07 {file} \x1b ]8;; \x07 " ) ) ) ;
scene
. ucmd ( )
. arg ( " --hyperlink=never " )
. succeeds ( )
. stdout_is ( format! ( " {file} \n " ) ) ;
}
2023-12-09 12:06:09 +00:00
#[ test ]
fn test_ls_color_do_not_reset ( ) {
let scene : TestScenario = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . mkdir ( " example " ) ;
at . mkdir ( " example/a " ) ;
at . mkdir ( " example/b " ) ;
let result = scene
. ucmd ( )
. arg ( " --color=always " )
. arg ( " example/ " )
. succeeds ( ) ;
// the second color code should not have a reset
assert_eq! (
result . stdout_str ( ) . escape_default ( ) . to_string ( ) ,
" \\ u{1b}[0m \\ u{1b}[01;34ma \\ u{1b}[0m \\ n \\ u{1b}[01;34mb \\ u{1b}[0m \\ n "
) ;
}
2023-12-15 21:49:35 +00:00
#[ cfg(all(unix, feature = " chmod " )) ]
#[ test ]
fn test_term_colorterm ( ) {
let scene = TestScenario ::new ( util_name! ( ) ) ;
let at = & scene . fixtures ;
at . touch ( " exe " ) ;
scene . ccmd ( " chmod " ) . arg ( " +x " ) . arg ( " exe " ) . succeeds ( ) ;
// Should show colors
let result = scene
. ucmd ( )
. arg ( " --color=always " )
. env ( " LS_COLORS " , " " )
. env ( " TERM " , " " )
. succeeds ( ) ;
assert_eq! (
result . stdout_str ( ) . trim ( ) . escape_default ( ) . to_string ( ) ,
" \\ u{1b}[0m \\ u{1b}[01;32mexe \\ u{1b}[0m "
) ;
// Should show colors
let result = scene
. ucmd ( )
. arg ( " --color=always " )
. env ( " LS_COLORS " , " " )
. env ( " COLORTERM " , " " )
. succeeds ( ) ;
assert_eq! (
result . stdout_str ( ) . trim ( ) . escape_default ( ) . to_string ( ) ,
" \\ u{1b}[0m \\ u{1b}[01;32mexe \\ u{1b}[0m "
) ;
// No colors
let result = scene
. ucmd ( )
. arg ( " --color=always " )
. env ( " LS_COLORS " , " " )
. env ( " TERM " , " " )
. env ( " COLORTERM " , " " )
. succeeds ( ) ;
assert_eq! (
result . stdout_str ( ) . trim ( ) . escape_default ( ) . to_string ( ) ,
" exe "
) ;
// No colors
let result = scene
. ucmd ( )
. arg ( " --color=always " )
. env ( " LS_COLORS " , " " )
. env ( " TERM " , " dumb " )
. env ( " COLORTERM " , " " )
. succeeds ( ) ;
assert_eq! (
result . stdout_str ( ) . trim ( ) . escape_default ( ) . to_string ( ) ,
" exe "
) ;
}