Files
fish-shell/tests/checks/path.fish
Nahor 944ab91fab tests: various fixes for Cygwin itself and ACL mounts
Most notably:
- Unlike MSYS, Cygwin seems to always properly handle symlinks (at least
in common scenarios)
- With ACL, "x" permission also requires "r" do to anything, be it files
or directories

Closes #12642
2026-04-19 17:09:36 +08:00

490 lines
12 KiB
Fish

#RUN: %fish %s
# The "path" builtin for dealing with paths
cygwin_nosymlinks && set nosymlinks
# Extension - for figuring out the file extension of a given path.
path extension /
or echo None
# CHECK:
# CHECK: None
# No extension
path extension /.
or echo Filename is just a dot, no extension
# CHECK:
# CHECK: Filename is just a dot, no extension
# No extension - ".foo" is the filename
path extension /.foo
or echo None again
# CHECK:
# CHECK: None again
path extension /foo
or echo None once more
# CHECK:
# CHECK: None once more
path extension /foo.txt
and echo Success
# CHECK: .txt
# CHECK: Success
path extension /foo.txt/bar
or echo Not even here
# CHECK:
# CHECK: Not even here
path extension . ..
or echo No extension
# CHECK:
# CHECK: No extension
path extension ./foo.mp4
# CHECK: .mp4
path extension ../banana
# CHECK:
# nothing, status 1
echo $status
# CHECK: 1
path extension ~/.config
# CHECK:
# nothing, status 1
echo $status
# CHECK: 1
path extension ~/.config.d
# CHECK: .d
path extension ~/.config.
echo $status
# status 0
# CHECK: .
# CHECK: 0
path change-extension '' ./foo.mp4
# CHECK: ./foo
path change-extension wmv ./foo.mp4
# CHECK: ./foo.wmv
path change-extension .wmv ./foo.mp4
# CHECK: ./foo.wmv
path change-extension '' ../banana
# CHECK: ../banana
# still status 0, because there was an argument
echo $status
# CHECK: 0
path change-extension '' ~/.config
# CHECK: {{.*}}/.config
echo $status
# CHECK: 0
path basename ./foo.mp4
# CHECK: foo.mp4
path basename ../banana
# CHECK: banana
path basename /usr/bin/
# CHECK: bin
path dirname ./foo.mp4
# CHECK: .
path basename ../banana
# CHECK: banana
path basename /usr/bin/
# CHECK: bin
cd $TMPDIR
cygwin_noacl ./ && set -l noacl
mkdir -p bin
touch bin/{bash,bssh,chsh,dash,fish,slsh,ssh,zsh}
ln -s $TMPDIR/bin/bash bin/sh
chmod +x bin/*
# We need files from here on
path filter bin argagagji
# The (hopefully) nonexistent argagagji is filtered implicitly:
# CHECK: bin
# With --invert, the existing bin is filtered
path filter --invert bin argagagji
# CHECK: argagagji
# With --invert and a type, bin fails the type,
# and argagagji doesn't exist, so both are printed.
path filter -vf bin argagagji
# CHECK: bin
# CHECK: argagagji
# With --all, return true if all paths are passed.
path filter --all bin bin/bash
echo $status
# CHECK: 0
path filter --all bin argagagji
echo $status
# CHECK: 1
# With --all and --invert, return true if none of paths is passed.
path filter --all --invert bin bin/bash
echo $status
# CHECK: 1
path filter --all --invert argagagji argagagji2
echo $status
# CHECK: 0
path filter --type file bin bin/fish
# Only fish is a file
# CHECK: bin/fish
chmod 500 bin/fish
path filter --type file,dir --perm exec,write bin/fish .
# fish is a file, which passes, and executable, which passes,
# but not writable, which fails.
#
# . is a directory and both writable and executable, typically.
# So it passes.
# CHECK: .
mkdir -p sbin
touch sbin/setuid-exe sbin/setgid-exe
# Without POSIX permission, there is no way to set the setuid bit, so fake
# the output.
if set -q noacl
echo sbin/setuid-exe
else
chmod u+s,a+x sbin/setuid-exe
path filter --perm suid sbin/*
end
# CHECK: sbin/setuid-exe
# Without POSIX permission, there is no way to set the setgid bit, so fake
# the result.
# And on at least FreeBSD on our CI this fails with "permission denied".
# So we can't test it, and we fake the output there too.
if set -q noacl
echo sbin/setgid-exe
else if chmod g+s,a+x sbin/setgid-exe 2>/dev/null
path filter --perm sgid sbin/*
else
echo sbin/setgid-exe
end
# CHECK: sbin/setgid-exe
mkdir stuff
touch stuff/{read,write,exec,readwrite,readexec,writeexec,all,none}
if set -q noacl
echo "#!/bin/sh" >stuff/exec
echo "#!/bin/sh" >stuff/readexec
echo "#!/bin/sh" >stuff/writeexec
echo "#!/bin/sh" >stuff/all
end
chmod 400 stuff/read
chmod 200 stuff/write
chmod 100 stuff/exec
chmod 600 stuff/readwrite
chmod 500 stuff/readexec
chmod 300 stuff/writeexec
chmod 700 stuff/all
chmod 000 stuff/none
# Validate that globs are sorted.
test (path filter stuff/* | path sort | string join ",") = (path filter stuff/* | string join ",")
# echo/CHECK to separate the output of different tests since they all blend
# together otherwise, making it hard to know which test failed.
echo "=== test --perm read"
# CHECK: === test --perm read
if set -q noacl
# noacl cannot mark files non-readable, filter out known bad files
path filter --perm read stuff/* | string match -rv ".*/(?:write|exec|writeexec|none).*"
else
path filter --perm read stuff/*
end
# CHECK: stuff/all
# CHECK: stuff/read
# CHECK: stuff/readexec
# CHECK: stuff/readwrite
echo "=== test -r"
# CHECK: === test -r
if set -q noacl
path filter -r stuff/* | string match -rv ".*/(?:write|exec|writeexec|none).*"
else
path filter -r stuff/*
end
# CHECK: stuff/all
# CHECK: stuff/read
# CHECK: stuff/readexec
# CHECK: stuff/readwrite
echo "=== test --perm write"
# CHECK: === test --perm write
path filter --perm write stuff/*
# CHECK: stuff/all
# CHECK: stuff/readwrite
# CHECK: stuff/write
# CHECK: stuff/writeexec
echo "=== test -w"
# CHECK: === test -w
path filter -w stuff/*
# CHECK: stuff/all
# CHECK: stuff/readwrite
# CHECK: stuff/write
# CHECK: stuff/writeexec
echo "=== test --perm exec"
# CHECK: === test --perm exec
begin
path filter --perm exec stuff/*
# Cygwin with ACL doesn't return non-readable files, so add them manually to pass the test
if __fish_is_cygwin && ! set -q noacl
echo stuff/exec
echo stuff/writeexec
end
end | sort
# CHECK: stuff/all
# CHECK: stuff/exec
# CHECK: stuff/readexec
# CHECK: stuff/writeexec
echo "=== test -x"
# CHECK: === test -x
begin
path filter -x exec stuff/*
if __fish_is_cygwin && ! set -q noacl
echo stuff/exec
echo stuff/writeexec
end
end | sort
# CHECK: stuff/all
# CHECK: stuff/exec
# CHECK: stuff/readexec
# CHECK: stuff/writeexec
echo "=== test --perm read,write"
# CHECK: === test --perm read,write
if set -q noacl
path filter --perm read,write stuff/* | string match -rv ".*/(?:write|writeexec).*"
else
path filter --perm read,write stuff/*
end
# CHECK: stuff/all
# CHECK: stuff/readwrite
echo "=== test --perm read,exec"
# CHECK: === test --perm read,exec
if set -q noacl
path filter --perm read,exec stuff/* | string match -rv ".*/(?:exec|writeexec).*"
else
path filter --perm read,exec stuff/*
end
# CHECK: stuff/all
# CHECK: stuff/readexec
echo "=== test --perm write,exec"
# CHECK: === test --perm write,exec
begin
path filter --perm write,exec stuff/*
if __fish_is_cygwin && ! set -q noacl
echo stuff/writeexec
end
end | sort
# CHECK: stuff/all
# CHECK: stuff/writeexec
echo "=== test --perm read,write,exec"
# CHECK: === test --perm read,write,exec
if set -q noacl
path filter --perm read,write,exec stuff/* | string match -rv ".*/(?:writeexec).*"
else
path filter --perm read,write,exec stuff/*
end
# CHECK: stuff/all
echo "=== test all"
# CHECK: === test all
path filter stuff/*
# CHECK: stuff/all
# CHECK: stuff/exec
# CHECK: stuff/none
# CHECK: stuff/read
# CHECK: stuff/readexec
# CHECK: stuff/readwrite
# CHECK: stuff/write
# CHECK: stuff/writeexec
path normalize /usr/bin//../../etc/fish
# The "//" is squashed and the ".." components neutralize the components before
# CHECK: /etc/fish
path normalize /bin//bash
# The "//" is squashed, but /bin isn't resolved even if your system links it to /usr/bin.
# CHECK: /bin/bash
# Paths with "-" get a "./":
path normalize -- -/foo -foo/foo
# CHECK: ./-/foo
# CHECK: ./-foo/foo
path normalize -- ../-foo
# CHECK: ../-foo
# This goes for filter as well
touch -- -foo
path filter -f -- -foo
# CHECK: ./-foo
# We need to remove the rest of the path because we have no idea what its value looks like.
if set -q nosymlinks
echo bin/bash
else
path resolve bin//sh | string match -r -- 'bin/bash$'
end
# The "//" is squashed, and the symlink is resolved.
# sh here is bash
# CHECK: bin/bash
# "../" cancels out even files.
path resolve bin//sh/../ | string match -r -- 'bin$'
# CHECK: bin
# `path resolve` with nonexistent paths
set -l path (path resolve foo/bar)
string match -rq "^"(pwd -P | string escape --style=regex)'/' -- $path
and echo It matches pwd!
or echo pwd is \'$PWD\' resolved path is \'$path\'
# CHECK: It matches pwd!
string replace -r "^"(pwd -P | string escape --style=regex)'/' "" -- $path
# CHECK: foo/bar
path resolve /banana//terracota/terracota/booooo/../pie
# CHECK: /banana/terracota/terracota/pie
path sort --key=basename {def,abc}/{456,123,789,abc,def,0} | path sort --key=dirname -r
# CHECK: def/0
# CHECK: def/123
# CHECK: def/456
# CHECK: def/789
# CHECK: def/abc
# CHECK: def/def
# CHECK: abc/0
# CHECK: abc/123
# CHECK: abc/456
# CHECK: abc/789
# CHECK: abc/abc
# CHECK: abc/def
path sort --unique --key=basename {def,abc}/{456,123,789} def/{abc,def,0} abc/{foo,bar,baz}
# CHECK: def/0
# CHECK: def/123
# CHECK: def/456
# CHECK: def/789
# CHECK: def/abc
# CHECK: abc/bar
# CHECK: abc/baz
# CHECK: def/def
# CHECK: abc/foo
# Symlink loop.
# It goes brrr.
if not set -q nosymlinks
ln -s target link
ln -s link target
end
if set -q nosymlinks ||
test (path resolve target) = (pwd -P)/target
echo target resolves to target
end
# CHECK: target resolves to target
if set -q nosymlinks ||
test (path resolve link) = (pwd -P)/link
echo link resolves to link
end
# CHECK: link resolves to link
# path mtime
# These tests deal with *time*, so we have to account
# for slow systems (like CI).
# So we should only test with a lot of slack.
echo bananana >>foo
test (math abs (date +%s) - (path mtime foo)) -lt 20
or echo MTIME IS BOGUS
sleep 2
set -l mtime (path mtime --relative foo)
test $mtime -ge 1
or echo mtime is too small
test $mtime -lt 20
or echo mtime is too large
touch -m -t 197001020000 epoch
set -l epochtime (path mtime epoch)
# Allow for timezone shenanigans
test $epochtime -gt 0 -a $epochtime -lt 180000
or echo Oops not mtime
path basename -Z foo bar baz | path sort
# CHECK: bar
# CHECK: baz
# CHECK: foo
path basename -E foo.txt /usr/local/foo.bar /foo.tar.gz
# CHECK: foo
# CHECK: foo
# CHECK: foo.tar
path basename --null-out bar baz | string escape
# CHECK: bar\x00baz\x00
path basename --quiet=foo
# CHECKERR: path basename: --quiet=foo: option does not take an argument
path basename --unknown-option
# CHECKERR: path basename: --unknown-option: unknown option
# CHECKERR: {{.*}}/checks/path.fish (line {{\d+}}):
# CHECKERR: path basename --unknown-option
# CHECKERR: ^
# CHECKERR: (Type 'help path' for related documentation)
path filter -t invalid_type
# CHECKERR: path filter: Invalid type 'invalid_type'
path filter -p 999
# CHECKERR: path filter: Invalid permission '999'
path sort --relative
# CHECKERR: path sort: --relative: unknown option
# CHECKERR: {{.*}}/checks/path.fish (line {{\d+}}):
# CHECKERR: path sort --relative
# CHECKERR: ^
# CHECKERR: (Type 'help path' for related documentation)
path change-extension
# CHECKERR: path change-extension: missing argument
echo some.file | path basename other.file
# CHECKERR: path basename: too many arguments
path sort --key=invalid-key
# CHECKERR: path sort: Invalid sort key 'invalid-key'
path
# CHECKERR: path: missing subcommand
# CHECKERR: {{.*}}/checks/path.fish (line {{\d+}}):
# CHECKERR: path
# CHECKERR: ^
# CHECKERR: (Type 'help path' for related documentation)
path invalid-subcmd
# CHECKERR: path invalid-subcmd: invalid subcommand
# CHECKERR: {{.*}}/checks/path.fish (line {{\d+}}):
# CHECKERR: path invalid-subcmd
# CHECKERR: ^
# CHECKERR: (Type 'help path' for related documentation)