435 lines
14 KiB
YAML
435 lines
14 KiB
YAML
name: "Extended tests"
|
|||
|
|
cases:
|
||
|
|
- name: "Extended test with redirect"
|
||
|
|
stdin: |
|
||
|
|
[[ $(echo hello >&2) ]] 2>/dev/null
|
||
|
|
echo "Result: $?"
|
||
|
|
|
||
|
|
- name: "File extended tests"
|
||
|
|
stdin: |
|
||
|
|
[[ -a /tmp ]] && echo "-a correctly checked /tmp"
|
||
|
|
[[ -a /some/non/existent/path ]] || echo "-a correctly checked non-existent path"
|
||
|
|
|
||
|
|
[[ -c /dev/null ]] && echo "-c correctly checked /dev/null"
|
||
|
|
[[ -c /tmp ]] || echo "-c correctly checked /tmp"
|
||
|
|
|
||
|
|
- name: "Existence tests"
|
||
|
|
stdin: |
|
||
|
|
[[ -e '' ]] || echo "-e correctly identified empty string as non-existent"
|
||
|
|
|
||
|
|
[[ -e non-existent ]] || echo "-e correctly identified non-existent path"
|
||
|
|
[[ ! -e non-existent ]] && echo "! -e correctly identified non-existent path"
|
||
|
|
|
||
|
|
touch test-file
|
||
|
|
[[ -e test-file ]] && echo "-e correctly identified existing non-dir"
|
||
|
|
|
||
|
|
mkdir test-dir
|
||
|
|
[[ -e test-dir ]] && echo "-d correctly identified existing dir"
|
||
|
|
|
||
|
|
- name: "Directory tests"
|
||
|
|
stdin: |
|
||
|
|
[[ -d '' ]] || echo "-d correctly identified empty string as non-existent"
|
||
|
|
|
||
|
|
[[ -d non-existent ]] || echo "-d correctly identified non-existent path"
|
||
|
|
[[ ! -d non-existent ]] && echo "! -d correctly identified non-existent path"
|
||
|
|
|
||
|
|
touch test-file
|
||
|
|
[[ -d test-file ]] || echo "-d correctly identified non-dir file"
|
||
|
|
|
||
|
|
mkdir test-dir
|
||
|
|
[[ -d test-dir ]] && echo "-d correctly identified existing dir"
|
||
|
|
|
||
|
|
- name: "File regular tests"
|
||
|
|
stdin: |
|
||
|
|
[[ -f '' ]] || echo "-f correctly identified empty string as non-existent"
|
||
|
|
|
||
|
|
[[ -f non-existent ]] || echo "-f correctly identified non-existent path"
|
||
|
|
[[ ! -f non-existent ]] && echo "! -f correctly identified non-existent path"
|
||
|
|
|
||
|
|
touch test-file
|
||
|
|
[[ -f test-file ]] && echo "-f correctly identified regular file"
|
||
|
|
|
||
|
|
mkdir test-dir
|
||
|
|
[[ -f test-dir ]] || echo "-f correctly identified directory as non-regular file"
|
||
|
|
|
||
|
|
- name: "File symbolic link tests"
|
||
|
|
stdin: |
|
||
|
|
[[ -L non-existent ]] || echo "-L correctly identified non-existent path"
|
||
|
|
|
||
|
|
touch test-file
|
||
|
|
[[ -L test-file ]] || echo "-L correctly identified non-link file"
|
||
|
|
|
||
|
|
ln -s test-file valid-link
|
||
|
|
[[ -L valid-link ]] && echo "-L correctly identified valid symbolic link"
|
||
|
|
|
||
|
|
ln -s non-existent-target dangling-link
|
||
|
|
[[ -L dangling-link ]] && echo "-L correctly identified dangling symbolic link"
|
||
|
|
|
||
|
|
- name: "File sticky bit tests"
|
||
|
|
stdin: |
|
||
|
|
touch test-file
|
||
|
|
[[ -k test-file ]] || echo "-k correctly identified file without sticky bit"
|
||
|
|
|
||
|
|
chmod 1600 test-file
|
||
|
|
[[ -k test-file ]] && echo "-k correctly identified file with sticky bit"
|
||
|
|
|
||
|
|
- name: "Fifo test"
|
||
|
|
stdin: |
|
||
|
|
touch test-file
|
||
|
|
[[ -p test-file ]] || echo "-p correctly identified non-fifo file"
|
||
|
|
|
||
|
|
mkfifo fifo-file
|
||
|
|
[[ -p test-file ]] && echo "-p correctly identified fifo file"
|
||
|
|
|
||
|
|
- name: "File executable tests"
|
||
|
|
stdin: |
|
||
|
|
[[ -x non-existent ]] || echo "-x correctly identified non-existent path"
|
||
|
|
|
||
|
|
touch test-file
|
||
|
|
ln -sf link test-file
|
||
|
|
[[ -x test-file ]] || echo "-x correctly identified non-executable file"
|
||
|
|
[[ -x link ]] || echo "-x correctly identified link to non-executable file"
|
||
|
|
|
||
|
|
chmod o+x test-file
|
||
|
|
[[ -x test-file ]] || echo "-x correctly identified other-only-executable file"
|
||
|
|
[[ -x link ]] || echo "-x correctly identified link to other-only-executable file"
|
||
|
|
|
||
|
|
chmod a+x test-file
|
||
|
|
[[ -x test-file ]] && echo "-x correctly identified executable file"
|
||
|
|
[[ -x link ]] && echo "-x correctly identified link to executable file"
|
||
|
|
|
||
|
|
- name: "File writable tests"
|
||
|
|
stdin: |
|
||
|
|
[[ -w non-existent ]] || echo "-w correctly identified non-existent path"
|
||
|
|
|
||
|
|
touch test-file
|
||
|
|
ln -sf link test-file
|
||
|
|
[[ -w test-file ]] || echo "-w correctly identified non-writable file"
|
||
|
|
[[ -w link ]] || echo "-w correctly identified link to non-writable file"
|
||
|
|
|
||
|
|
chmod o+x test-file
|
||
|
|
[[ -w test-file ]] || echo "-w correctly identified other-only-writable file"
|
||
|
|
[[ -w link ]] || echo "-w correctly identified link to other-only-writable file"
|
||
|
|
|
||
|
|
chmod a+x test-file
|
||
|
|
[[ -w test-file ]] && echo "-w correctly identified writable file"
|
||
|
|
[[ -w link ]] && echo "-w correctly identified link to writable file"
|
||
|
|
|
||
|
|
- name: "Variable set and nameref tests"
|
||
|
|
stdin: |
|
||
|
|
foo="bar"
|
||
|
|
declare -n bang=foo
|
||
|
|
[[ -R bang ]] && echo "-R correctly identified nameref variable that is set"
|
||
|
|
|
||
|
|
declare -n somevar
|
||
|
|
[[ -R somevar ]] || echo "-R correctly identified nameref variable that isn't set"
|
||
|
|
|
||
|
|
- name: "Files refer to same device and inode tests"
|
||
|
|
stdin: |
|
||
|
|
[[ /bin/sh -ef /bin/sh ]] && echo "-ef correctly identified device and inode numbers"
|
||
|
|
|
||
|
|
[[ ! /etc/os-release -ef /bin/sh ]] && echo "-ef correctly identified device and inode numbers that do not match"
|
||
|
|
|
||
|
|
- name: "File is newer"
|
||
|
|
stdin: |
|
||
|
|
touch -d "2 hours ago" bar
|
||
|
|
touch foo
|
||
|
|
|
||
|
|
[[ foo -nt bar ]] && echo "-nt correctly identified newer file"
|
||
|
|
[[ foo -nt foo ]] && echo "-nt incorrectly identified file as newer than itself"
|
||
|
|
[[ foo -nt file_no_exists ]] && echo "-nt correctly identified when file2 does not exist"
|
||
|
|
|
||
|
|
- name: "File is older"
|
||
|
|
stdin: |
|
||
|
|
touch -d "2 hours ago" foo
|
||
|
|
touch bar
|
||
|
|
|
||
|
|
[[ foo -ot bar ]] && echo "-ot correctly identified older file"
|
||
|
|
[[ foo -ot foo ]] && echo "-ot incorrectly identified file as older than itself"
|
||
|
|
[[ file_no_exists -ot foo ]] && echo "-ot correctly identified when file1 does not exist"
|
||
|
|
|
||
|
|
- name: "Unary string extended tests"
|
||
|
|
stdin: |
|
||
|
|
[[ -z "" ]] && echo "-z: Pass"
|
||
|
|
[[ -z "something" ]] && echo "-z: Fail"
|
||
|
|
|
||
|
|
[[ -n "something" ]] && echo "-n: Pass"
|
||
|
|
[[ -n "" ]] && echo "-n: Fail"
|
||
|
|
|
||
|
|
- name: "Shell option extended tests"
|
||
|
|
stdin: |
|
||
|
|
set -o emacs
|
||
|
|
[[ -o emacs ]] && echo "1: option enabled"
|
||
|
|
|
||
|
|
set +o emacs
|
||
|
|
[[ -o emacs ]] && echo "2: option enabled"
|
||
|
|
|
||
|
|
- name: "Binary string extended tests"
|
||
|
|
stdin: |
|
||
|
|
[[ "" == "" ]] && echo "1. Pass"
|
||
|
|
[[ "" = "" ]] && echo "2. Pass"
|
||
|
|
[[ "" != "" ]] && echo "3. Fail"
|
||
|
|
|
||
|
|
[[ "a" != "b" ]] && echo "4. Pass"
|
||
|
|
[[ "a" = "b" ]] && echo "5. Fail"
|
||
|
|
[[ "a" == "b" ]] && echo "6. Fail"
|
||
|
|
|
||
|
|
[[ "a" < "b" ]] && echo "7. Pass"
|
||
|
|
[[ "a" < "a" ]] && echo "8. Fail"
|
||
|
|
[[ "b" < "a" ]] && echo "9. Fail"
|
||
|
|
[[ "a" > "b" ]] && echo "10. Fail"
|
||
|
|
[[ "a" > "a" ]] && echo "11. Fail"
|
||
|
|
[[ "b" > "a" ]] && echo "12. Pass"
|
||
|
|
|
||
|
|
- name: "Binary string matching"
|
||
|
|
stdin: |
|
||
|
|
[[ "abc" == a* ]] && echo "1. Pass"
|
||
|
|
[[ "abc" != a* ]] && echo "2. Fail"
|
||
|
|
[[ a* != "abc" ]] && echo "3. Pass"
|
||
|
|
|
||
|
|
- name: "Binary string matching with expansion"
|
||
|
|
stdin: |
|
||
|
|
exclude="0123456789"
|
||
|
|
[[ "clue" == +([$exclude]) ]] && echo "1. Fail"
|
||
|
|
[[ "8675309" == +([$exclude]) ]] && echo "2. Pass"
|
||
|
|
|
||
|
|
- name: "Quoted pattern binary string matching"
|
||
|
|
stdin: |
|
||
|
|
[[ "abc" == "a*" ]] && echo "1. Matches"
|
||
|
|
[[ "abc" != "a*" ]] && echo "2. Matches"
|
||
|
|
|
||
|
|
- name: "Tilde binary string matching"
|
||
|
|
stdin: |
|
||
|
|
x='~/'
|
||
|
|
[[ $x == ~* ]] && echo "1. Matches"
|
||
|
|
|
||
|
|
- name: "Arithmetic extended tests"
|
||
|
|
stdin: |
|
||
|
|
[[ 0 -eq 0 ]] && echo "1. Pass"
|
||
|
|
[[ 0 -ne 0 ]] && echo "2. Fail"
|
||
|
|
[[ 0 -lt 0 ]] && echo "3. Fail"
|
||
|
|
[[ 0 -le 0 ]] && echo "4. Pass"
|
||
|
|
[[ 0 -gt 0 ]] && echo "5. Fail"
|
||
|
|
[[ 0 -ge 0 ]] && echo "6. Pass"
|
||
|
|
|
||
|
|
[[ 0 -eq 1 ]] && echo "7. Fail"
|
||
|
|
[[ 0 -ne 1 ]] && echo "8. Pass"
|
||
|
|
[[ 0 -lt 1 ]] && echo "9. Pass"
|
||
|
|
[[ 0 -le 1 ]] && echo "10. Pass"
|
||
|
|
[[ 0 -gt 1 ]] && echo "11. Fail"
|
||
|
|
[[ 0 -ge 1 ]] && echo "12. Fail"
|
||
|
|
|
||
|
|
[[ 1 -eq 0 ]] && echo "13. Fail"
|
||
|
|
[[ 1 -ne 0 ]] && echo "14. Pass"
|
||
|
|
[[ 1 -lt 0 ]] && echo "15. Fail"
|
||
|
|
[[ 1 -le 0 ]] && echo "16. Fail"
|
||
|
|
[[ 1 -gt 0 ]] && echo "17. Pass"
|
||
|
|
[[ 1 -ge 0 ]] && echo "18. Pass"
|
||
|
|
|
||
|
|
- name: "Regex"
|
||
|
|
stdin: |
|
||
|
|
[[ "a" =~ ^a$ ]] && echo "1. Pass"
|
||
|
|
[[ "abc" =~ a* ]] && echo "2. Pass"
|
||
|
|
[[ a =~ ^(a)$ ]] && echo "3. Pass"
|
||
|
|
[[ a =~ ^(a|b)$ ]] && echo "4. Pass"
|
||
|
|
[[ a =~ c ]] && echo "5. Pass"
|
||
|
|
|
||
|
|
- name: "Regex with case insensitivity"
|
||
|
|
stdin: |
|
||
|
|
shopt -u nocasematch
|
||
|
|
[[ "a" =~ A ]] && echo "1. Pass"
|
||
|
|
|
||
|
|
shopt -s nocasematch
|
||
|
|
[[ "a" =~ A ]] && echo "1. Pass"
|
||
|
|
|
||
|
|
- name: "Regex with capture"
|
||
|
|
stdin: |
|
||
|
|
pattern='(Hello), ([a-z]+)\.'
|
||
|
|
if [[ "Hello, world." =~ ${pattern} ]]; then
|
||
|
|
echo "Match found!"
|
||
|
|
for i in "${!BASH_REMATCH[@]}"; do
|
||
|
|
echo "$i: '${BASH_REMATCH[$i]}'"
|
||
|
|
done
|
||
|
|
fi
|
||
|
|
|
||
|
|
- name: "Regex with capture including optional matches"
|
||
|
|
stdin: |
|
||
|
|
pattern='(Hello)(,?) ([a-z]+)\.'
|
||
|
|
if [[ "Hello world." =~ ${pattern} ]]; then
|
||
|
|
echo "Match found!"
|
||
|
|
for i in "${!BASH_REMATCH[@]}"; do
|
||
|
|
echo "$i: '${BASH_REMATCH[$i]}'"
|
||
|
|
done
|
||
|
|
fi
|
||
|
|
|
||
|
|
- name: "Regex with quoting"
|
||
|
|
stdin: |
|
||
|
|
regex="^$"
|
||
|
|
|
||
|
|
# TODO(test): The commented out lines appear differ in behavior between versions of bash.
|
||
|
|
# [[ "" =~ '' ]] && echo "1. Matched"
|
||
|
|
[[ "" =~ '^$' ]] && echo "2. Matched"
|
||
|
|
# [[ "" =~ "" ]] && echo "3. Matched"
|
||
|
|
[[ "" =~ "^$" ]] && echo "4. Matched"
|
||
|
|
[[ "" =~ ^$ ]] && echo "5. Matched"
|
||
|
|
[[ "" =~ ${regex} ]] && echo "6. Matched"
|
||
|
|
[[ "" =~ "${regex}" ]] && echo "7. Matched"
|
||
|
|
|
||
|
|
- name: "Regex with escaping"
|
||
|
|
stdin: |
|
||
|
|
[[ '' =~ ^\$$ ]] && echo "1. Matched"
|
||
|
|
|
||
|
|
- name: "Regex with double parens"
|
||
|
|
stdin: |
|
||
|
|
if [[ xy =~ x+((y)) ]]; then
|
||
|
|
echo "Matches"
|
||
|
|
fi
|
||
|
|
|
||
|
|
- name: "Regex with special chars in parens"
|
||
|
|
stdin: |
|
||
|
|
[[ "<" =~ (<) ]] && echo "1. Matched"
|
||
|
|
[[ ">" =~ (<) ]] && echo "2. Matched"
|
||
|
|
|
||
|
|
- name: "Regex with spaces inside parens"
|
||
|
|
stdin: |
|
||
|
|
[[ "x" =~ (a| *) ]] && echo "1. Matched"
|
||
|
|
[[ "a b" =~ ^(a |b) ]] && echo "2. Matched: [${BASH_REMATCH[0]}]"
|
||
|
|
[[ "a b" =~ ^(a |b)$ ]] || echo "3. No match"
|
||
|
|
[[ " " =~ ^( )$ ]] && echo "4. Matched: [${BASH_REMATCH[1]}]"
|
||
|
|
|
||
|
|
- name: "Regex with unescaped open bracket in character class"
|
||
|
|
stdin: |
|
||
|
|
[[ "[" =~ ^([x[]) ]] && echo "Matched"
|
||
|
|
|
||
|
|
- name: "Empty and space checks"
|
||
|
|
stdin: |
|
||
|
|
check() {
|
||
|
|
var="$1"
|
||
|
|
[[ ${var} && ! ${var//[[:space:]]/} ]]
|
||
|
|
}
|
||
|
|
|
||
|
|
check "" && echo "1. Only space"
|
||
|
|
check " " && echo "2. Only space"
|
||
|
|
check $'\t' && echo "3. Only space"
|
||
|
|
check " a " && echo "4. Only space"
|
||
|
|
|
||
|
|
- name: "Newlines in test expression"
|
||
|
|
stdin: |
|
||
|
|
[[
|
||
|
|
"a" == "a"
|
||
|
|
&&
|
||
|
|
"b" == "b"
|
||
|
|
]] && echo "Succeeded"
|
||
|
|
|
||
|
|
- name: "Variable set checks"
|
||
|
|
stdin: |
|
||
|
|
declare set_but_no_value
|
||
|
|
[[ -v set_but_no_value ]] && echo "1. Set but no value"
|
||
|
|
|
||
|
|
declare set_with_value=xyz
|
||
|
|
[[ -v set_with_value ]] && echo "2. Set with value"
|
||
|
|
|
||
|
|
[[ -v not_set ]] || echo "3. Not set"
|
||
|
|
|
||
|
|
- name: "Variables in extended tests"
|
||
|
|
stdin: |
|
||
|
|
var=10
|
||
|
|
|
||
|
|
[[ $var -eq 10 ]] && echo "1. Pass"
|
||
|
|
[[ var -eq 10 ]] && echo "2. Pass"
|
||
|
|
|
||
|
|
- name: "Regex with min/max counts"
|
||
|
|
stdin: |
|
||
|
|
[[ z =~ ^z{2,6}$ ]] && echo "1. Matches"
|
||
|
|
[[ zzzz =~ ^z{2,6}$ ]] && echo "2. Matches"
|
||
|
|
[[ zzzzzzzzz =~ ^z{2,6}$ ]] && echo "3. Matches"
|
||
|
|
|
||
|
|
- name: "Regex with newline"
|
||
|
|
stdin: |
|
||
|
|
[[ $'\n' =~ . ]] && echo "1. Matches"
|
||
|
|
|
||
|
|
- name: "Arithmetic comparisons with whitespace in operands"
|
||
|
|
stdin: |
|
||
|
|
# Leading/trailing whitespace should be trimmed before comparison
|
||
|
|
[[ 0 -eq ' 0' ]] && echo "1. leading space"
|
||
|
|
[[ 0 -eq '0 ' ]] && echo "2. trailing space"
|
||
|
|
[[ 0 -eq ' 0 ' ]] && echo "3. both spaces"
|
||
|
|
|
||
|
|
# Tabs
|
||
|
|
[[ 0 -eq ' 0' ]] && echo "4. leading tab"
|
||
|
|
[[ 0 -eq '0 ' ]] && echo "5. trailing tab"
|
||
|
|
|
||
|
|
# Newlines
|
||
|
|
[[ 0 -eq ' 0
|
||
|
|
' ]] && echo "6. newline"
|
||
|
|
|
||
|
|
# Other operators
|
||
|
|
[[ 5 -lt ' 10' ]] && echo "7. -lt with space"
|
||
|
|
[[ 10 -gt ' 5' ]] && echo "8. -gt with space"
|
||
|
|
[[ 5 -ne ' 6' ]] && echo "9. -ne with space"
|
||
|
|
[[ 5 -le ' 5' ]] && echo "10. -le with space"
|
||
|
|
[[ 5 -ge ' 5' ]] && echo "11. -ge with space"
|
||
|
|
|
||
|
|
# Negative numbers with whitespace
|
||
|
|
[[ -5 -eq ' -5' ]] && echo "12. negative with space"
|
||
|
|
|
||
|
|
- name: "Terminal fd check with whitespace"
|
||
|
|
stdin: |
|
||
|
|
# -t operator should trim whitespace before parsing fd number
|
||
|
|
# These should all behave identically (false since not a terminal)
|
||
|
|
[[ -t 0 ]]
|
||
|
|
r1=$?
|
||
|
|
[[ -t ' 0' ]]
|
||
|
|
r2=$?
|
||
|
|
[[ -t '0 ' ]]
|
||
|
|
r3=$?
|
||
|
|
|
||
|
|
# All should have the same result
|
||
|
|
[[ $r1 -eq $r2 && $r2 -eq $r3 ]] && echo "All -t results match"
|
||
|
|
|
||
|
|
- name: "Nameref -R with attribute removal"
|
||
|
|
stdin: |
|
||
|
|
target="value"
|
||
|
|
declare -n ref=target
|
||
|
|
[[ -R ref ]] && echo "ref is a nameref"
|
||
|
|
[[ -R target ]] && echo "target is a nameref" || echo "target is not a nameref"
|
||
|
|
declare +n ref
|
||
|
|
[[ -R ref ]] && echo "still nameref" || echo "no longer a nameref"
|
||
|
|
|
||
|
|
- name: "Nameref -v follows nameref"
|
||
|
|
known_failure: true
|
||
|
|
stdin: |
|
||
|
|
target="value"
|
||
|
|
declare -n ref=target
|
||
|
|
[[ -v ref ]] && echo "ref is set (via target)"
|
||
|
|
unset target
|
||
|
|
[[ -v ref ]] && echo "ref still set" || echo "ref not set (target gone)"
|
||
|
|
|
||
|
|
- name: "Nameref -v to array element treats resolved name literally"
|
||
|
|
known_failure: true
|
||
|
|
stdin: |
|
||
|
|
arr=(a b c d)
|
||
|
|
declare -n ref='arr[2]'
|
||
|
|
[[ -v ref ]] && echo "set" || echo "unset"
|
||
|
|
|
||
|
|
- name: "Nameref -v with explicit subscript on nameref to whole array"
|
||
|
|
# TODO(nameref): brush doesn't yet handle [[ -v "ref[N]" ]] where ref is
|
||
|
|
# a nameref to a whole array. Bash resolves ref→arr and then checks arr[2],
|
||
|
|
# but brush currently treats the subscript as part of the nameref target
|
||
|
|
# string rather than applying it after resolution. The fix belongs in
|
||
|
|
# extendedtests.rs (ShellVariableIsSetAndAssigned) — the operand "ref[2]"
|
||
|
|
# needs to be split into name="ref" + subscript="2", the nameref resolved
|
||
|
|
# on the name portion, then the subscript applied to the resolved target.
|
||
|
|
known_failure: true
|
||
|
|
stdin: |
|
||
|
|
arr=(a b c d)
|
||
|
|
declare -n ref2=arr
|
||
|
|
[[ -v "ref2[2]" ]] && echo "set" || echo "unset"
|
||
|
|
|
||
|
|
- name: "Nameref -v to nonexistent array index"
|
||
|
|
known_failure: true
|
||
|
|
stdin: |
|
||
|
|
arr=(a b c)
|
||
|
|
declare -n ref='arr[5]'
|
||
|
|
[[ -v ref ]] && echo "set" || echo "unset"
|