diff --git a/tests/checks/string.fish b/tests/checks/string.fish new file mode 100644 index 000000000..c27b6b6c1 --- /dev/null +++ b/tests/checks/string.fish @@ -0,0 +1,555 @@ +#RUN: %fish %s +# Tests for string builtin. Mostly taken from man page examples. + +string match -r -v "c.*" dog can cat diz; and echo "exit 0" +# CHECK: dog +# CHECK: diz +# CHECK: exit 0 + +string match -q -r -v "c.*" dog can cat diz; and echo "exit 0" +# CHECK: exit 0 + +string match -v "c*" dog can cat diz; and echo "exit 0" +# CHECK: dog +# CHECK: diz +# CHECK: exit 0 + +string match -q -v "c*" dog can cat diz; and echo "exit 0" +# CHECK: exit 0 + +string match -v "d*" dog dan dat diz; or echo "exit 1" +# CHECK: exit 1 + +string match -q -v "d*" dog dan dat diz; or echo "exit 1" +# CHECK: exit 1 + +string match -r -v x y; and echo "exit 0" +# CHECK: y +# CHECK: exit 0 + +string match -r -v x x; or echo "exit 1" +# CHECK: exit 1 + +string match -q -r -v x y; and echo "exit 0" +# CHECK: exit 0 + +string match -q -r -v x x; or echo "exit 1" +# CHECK: exit 1 + +string length "hello, world" +# CHECK: 12 + +string length -q ""; and echo not zero length; or echo zero length +# CHECK: zero length + +string sub --length 2 abcde +# CHECK: ab + +string sub -s 2 -l 2 abcde +# CHECK: bc + +string sub --start=-2 abcde +# CHECK: de + +string split . example.com +# CHECK: example +# CHECK: com + +string split -r -m1 / /usr/local/bin/fish +# CHECK: /usr/local/bin +# CHECK: fish + +string split "" abc +# CHECK: a +# CHECK: b +# CHECK: c + +seq 3 | string join ... +# CHECK: 1...2...3 + +string trim " abc " +# CHECK: abc + +string trim --right --chars=yz xyzzy zany +# CHECK: x +# CHECK: zan + +echo \x07 | string escape +# CHECK: \cg + +string escape --style=script 'a b#c"\'d' +# CHECK: a\ b\#c\"\'d + +string escape --style=url 'a b#c"\'d' +# CHECK: a%20b%23c%22%27d + +string escape --style=url \na\nb%c~d\n +# CHECK: %0Aa%0Ab%25c~d%0A + +string escape --style=var 'a b#c"\'d' +# CHECK: a_20_b_23_c_22_27_d + +string escape --style=var a\nghi_ +# CHECK: a_0A_ghi__ + +string escape --style=var 'abc' +# CHECK: abc + +string escape --style=var '_a_b_c_' +# CHECK: __a__b__c__ + +string escape --style=var -- - +# CHECK: _2D_ + +# string escape with multibyte chars +string escape --style=url aöb +string escape --style=url 中 +string escape --style=url aöb | string unescape --style=url +string escape --style=url 中 | string unescape --style=url + +string escape --style=var aöb +string escape --style=var 中 +string escape --style=var aöb | string unescape --style=var +string escape --style=var 中 | string unescape --style=var +# CHECK: a%C3%B6b +# CHECK: %E4%B8%AD +# CHECK: aöb +# CHECK: 中 +# CHECK: a_C3_B6_b +# CHECK: _E4_B8_AD_ +# CHECK: aöb +# CHECK: 中 + +# test regex escaping +string escape --style=regex ".ext" +string escape --style=regex "bonjour, amigo" +string escape --style=regex "^this is a literal string" +# CHECK: \.ext +# CHECK: bonjour, amigo +# CHECK: \^this is a literal string + +### Verify that we can correctly unescape the same strings +# we tested escaping above. +set x (string unescape (echo \x07 | string escape)) +test $x = \x07 +and echo success +# CHECK: success + +string unescape --style=script (string escape --style=script 'a b#c"\'d') +# CHECK: a b#c"'d + +string unescape --style=url (string escape --style=url 'a b#c"\'d') +# CHECK: a b#c"'d + +string unescape --style=url (string escape --style=url \na\nb%c~d\n) +# CHECK: +# CHECK: a +# CHECK: b%c~d + +string unescape --style=var (string escape --style=var 'a b#c"\'d') +# CHECK: a b#c"'d + +string unescape --style=var (string escape --style=var a\nghi_) +# CHECK: a +# CHECK: ghi_ + +string unescape --style=var (string escape --style=var 'abc') +# CHECK: abc + +string unescape --style=var (string escape --style=var '_a_b_c_') +# CHECK: _a_b_c_ + +string unescape --style=var -- (string escape --style=var -- -) +# CHECK: - + +### Verify that we can correctly match strings. +string match "*" a +# CHECK: a + +string match "a*b" axxb +# CHECK: axxb + +string match -i "a**B" Axxb +# CHECK: Axxb + +echo "ok?" | string match "*?" +# CHECK: ok? + +string match -r "cat|dog|fish" "nice dog" +# CHECK: dog + +string match -r "(\d\d?):(\d\d):(\d\d)" 2:34:56 +# CHECK: 2:34:56 +# CHECK: 2 +# CHECK: 34 +# CHECK: 56 + +string match -r "^(\w{2,4})\g1\$" papa mud murmur +# CHECK: papa +# CHECK: pa +# CHECK: murmur +# CHECK: mur + +string match -r -a -n at ratatat +# CHECK: 2 2 +# CHECK: 4 2 +# CHECK: 6 2 + +string match -r -i "0x[0-9a-f]{1,8}" "int magic = 0xBadC0de;" +# CHECK: 0xBadC0de + +string replace is was "blue is my favorite" +# CHECK: blue was my favorite + +string replace 3rd last 1st 2nd 3rd +# CHECK: 1st +# CHECK: 2nd +# CHECK: last + +string replace -a " " _ "spaces to underscores" +# CHECK: spaces_to_underscores + +string replace -r -a "[^\d.]+" " " "0 one two 3.14 four 5x" +# CHECK: 0 3.14 5 + +string replace -r "(\w+)\s+(\w+)" "\$2 \$1 \$\$" "left right" +# CHECK: right left $ + +string replace -r "\s*newline\s*" "\n" "put a newline here" +# CHECK: put a +# CHECK: here + +string replace -r -a "(\w)" "\$1\$1" ab +# CHECK: aabb + +string replace --filter x X abc axc x def jkx +or echo Unexpected exit status at line (status --current-line-number) +# CHECK: aXc +# CHECK: X +# CHECK: jkX + +string replace --filter y Y abc axc x def jkx +and echo Unexpected exit status at line (status --current-line-number) + +string replace --regex -f "\d" X 1bc axc 2 d3f jk4 xyz +or echo Unexpected exit status at line (status --current-line-number) +# CHECK: Xbc +# CHECK: X +# CHECK: dXf +# CHECK: jkX + +string replace --regex -f "Z" X 1bc axc 2 d3f jk4 xyz +and echo Unexpected exit status at line (status --current-line-number) + +# From https://github.com/fish-shell/fish-shell/issues/5201 +# 'string match -r with empty capture groups' +string match -r '^([ugoa]*)([=+-]?)([rwx]*)$' '=r' +#CHECK: =r +#CHECK: +#CHECK: = +#CHECK: r + +### Test some failure cases +string match -r "[" "a[sd"; and echo "unexpected exit 0" +# CHECKERR: string match: Regular expression compile error: missing terminating ] for character class +# CHECKERR: string match: [ +# CHECKERR: string match: ^ + +# FIXME: This prints usage summary? +#string invalidarg; and echo "unexpected exit 0" +# DONTCHECKERR: string: Subcommand 'invalidarg' is not valid + +string length; or echo "missing argument returns 1" +# CHECK: missing argument returns 1 + +string match -r -v "[dcantg].*" dog can cat diz; or echo "no regexp invert match" +# CHECK: no regexp invert match + +string match -v "*" dog can cat diz; or echo "no glob invert match" +# CHECK: no glob invert match + +string match -rvn a bbb; or echo "exit 1" +# CHECK: 1 3 + +### Test repeat subcommand +string repeat -n 2 "foo" +# CHECK: foofoo + +string repeat --count 2 "foo" +# CHECK: foofoo + +echo foo | string repeat -n 2 +# CHECK: foofoo + +string repeat -n2 -q "foo"; and echo "exit 0" +# CHECK: exit 0 + +string repeat -n2 --quiet "foo"; and echo "exit 0" +# CHECK: exit 0 + +string repeat -n0 "foo"; or echo "exit 1" +# CHECK: exit 1 + +string repeat -n0; or echo "exit 1" +# CHECK: exit 1 + +string repeat -m0; or echo "exit 1" +# CHECK: exit 1 + +string repeat -n1 -N "there is "; echo "no newline" +# CHECK: there is no newline + +string repeat -n1 --no-newline "there is "; echo "no newline" +# CHECK: there is no newline + +string repeat -n10 -m4 "foo" +# CHECK: foof + +string repeat -n10 --max 5 "foo" +# CHECK: foofo + +string repeat -n3 -m20 "foo" +# CHECK: foofoofoo + +string repeat -m4 "foo" +# CHECK: foof + +string repeat -n-1 "foo"; and echo "exit 0" +# CHECKERR: string repeat: Invalid count value '-1' + +string repeat -m-1 "foo"; and echo "exit 0" +# CHECKERR: string repeat: Invalid max value '-1' + +string repeat -n notanumber "foo"; and echo "exit 0" +# CHECKERR: string repeat: Argument 'notanumber' is not a number + +string repeat -m notanumber "foo"; and echo "exit 0" +# CHECKERR: string repeat: Argument 'notanumber' is not a number + +echo "stdin" | string repeat -n1 "and arg"; and echo "exit 0" +# CHECKERR: string repeat: Too many arguments + +string repeat -n; and echo "exit 0" +# CHECKERR: string repeat: Expected argument + +# FIXME: Also triggers usage +# string repeat -l fakearg +# DONTCHECKERR: string repeat: Unknown option '-l' + +string repeat "" +or echo string repeat empty string failed +# CHECK: string repeat empty string failed + +string repeat -n3 "" +or echo string repeat empty string failed +# CHECK: string repeat empty string failed + +# Test equivalent matches with/without the --entire, --regex, and --invert flags. +string match -e x abc dxf xyz jkx x z +or echo exit 1 +# CHECK: dxf +# CHECK: xyz +# CHECK: jkx +# CHECK: x + +string match x abc dxf xyz jkx x z +# CHECK: x + +string match --entire -r "a*b[xy]+" abc abxc bye aaabyz kaabxz abbxy abcx caabxyxz +or echo exit 1 +# CHECK: abxc +# CHECK: bye +# CHECK: aaabyz +# CHECK: kaabxz +# CHECK: abbxy +# CHECK: caabxyxz + +# 'string match --entire "" -- banana' +string match --entire "" -- banana +or echo exit 1 +# CHECK: banana + +# 'string match -r "a*b[xy]+" abc abxc bye aaabyz kaabxz abbxy abcx caabxyxz' +string match -r "a*b[xy]+" abc abxc bye aaabyz kaabxz abbxy abcx caabxyxz +or echo exit 1 +# CHECK: abx +# CHECK: by +# CHECK: aaaby +# CHECK: aabx +# CHECK: bxy +# CHECK: aabxyx + +# Make sure that groups are handled correct with/without --entire. +# 'string match --entire -r "a*b([xy]+)" abc abxc bye aaabyz kaabxz abbxy abcx caabxyxz' +string match --entire -r "a*b([xy]+)" abc abxc bye aaabyz kaabxz abbxy abcx caabxyxz +or echo exit 1 +# CHECK: abxc +# CHECK: x +# CHECK: bye +# CHECK: y +# CHECK: aaabyz +# CHECK: y +# CHECK: kaabxz +# CHECK: x +# CHECK: abbxy +# CHECK: xy +# CHECK: caabxyxz +# CHECK: xyx + +# 'string match -r "a*b([xy]+)" abc abxc bye aaabyz kaabxz abbxy abcx caabxyxz' +string match -r "a*b([xy]+)" abc abxc bye aaabyz kaabxz abbxy abcx caabxyxz +or echo exit 1 +# CHECK: abx +# CHECK: x +# CHECK: by +# CHECK: y +# CHECK: aaaby +# CHECK: y +# CHECK: aabx +# CHECK: x +# CHECK: bxy +# CHECK: xy +# CHECK: aabxyx +# CHECK: xyx + +# Test `string lower` and `string upper`. +set x (string lower abc DEF gHi) +or echo string lower exit 1 +test $x[1] = 'abc' -a $x[2] = 'def' -a $x[3] = 'ghi' +or echo strings not converted to lowercase + +set x (echo abc DEF gHi | string lower) +or echo string lower exit 1 +test $x[1] = 'abc def ghi' +or echo strings not converted to lowercase + +string lower -q abc +and echo lowercasing a lowercase string did not fail as expected + +set x (string upper abc DEF gHi) +or echo string upper exit 1 +test $x[1] = 'ABC' -a $x[2] = 'DEF' -a $x[3] = 'GHI' +or echo strings not converted to uppercase + +set x (echo abc DEF gHi | string upper) +or echo string upper exit 1 +test $x[1] = 'ABC DEF GHI' +or echo strings not converted to uppercase + +string upper -q ABC DEF +and echo uppercasing a uppercase string did not fail as expected + +# 'Check NUL' +# Note: We do `string escape` at the end to make a `\0` literal visible. +printf 'a\0b' | string escape +printf 'a\0c' | string match -e 'a' | string escape +printf 'a\0d' | string split '' | string escape +printf 'a\0b' | string match -r '.*b$' | string escape +printf 'a\0b' | string replace b g | string escape +printf 'a\0b' | string replace -r b g | string escape +# TODO: These do not yet work! +# printf 'a\0b' | string match '*b' | string escape +# CHECK: a\x00b +# CHECK: a\x00c +# CHECK: a +# CHECK: \x00 +# CHECK: d +# CHECK: a\x00b +# CHECK: a\x00g +# CHECK: a\x00g + +# string split0 +count (echo -ne 'abcdefghi' | string split0) +# CHECK: 1 +count (echo -ne 'abc\x00def\x00ghi\x00' | string split0) +# CHECK: 3 +count (echo -ne 'abc\x00def\x00ghi\x00\x00' | string split0) +# CHECK: 4 +count (echo -ne 'abc\x00def\x00ghi' | string split0) +# CHECK: 3 +count (echo -ne 'abc\ndef\x00ghi\x00' | string split0) +# CHECK: 2 +count (echo -ne 'abc\ndef\nghi' | string split0) +# CHECK: 1 +# #5701 - split0 always returned 1 +echo -ne 'a\x00b' | string split0 +and echo Split something +# CHECK: a +# CHECK: b +# CHECK: Split something + +# string join0 +set tmp beta alpha\ngamma +count (string join \n $tmp) +# CHECK: 3 +count (string join0 $tmp) +# CHECK: 2 +count (string join0 $tmp | string split0) +# CHECK: 2 + +# string split0 in functions +# This function outputs some newline-separated content, and some +# explicitly separated content. +function dualsplit + echo alpha + echo beta + echo -ne 'gamma\x00delta' | string split0 +end +count (dualsplit) +# CHECK: 4 + +# string collect +count (echo one\ntwo\nthree\nfour | string collect) +count (echo one | string collect) +# CHECK: 1 +# CHECK: 1 +echo [(echo one\ntwo\nthree | string collect)] +# CHECK: [one +# CHECK: two +# CHECK: three] +echo [(echo one\ntwo\nthree | string collect -N)] +# CHECK: [one +# CHECK: two +# CHECK: three +# CHECK: ] +printf '[%s]\n' (string collect one\n\n two\n) +# CHECK: [one] +# CHECK: [two] +printf '[%s]\n' (string collect -N one\n\n two\n) +# CHECK: [one +# CHECK: +# CHECK: ] +# CHECK: [two +# CHECK: ] +printf '[%s]\n' (string collect --no-trim-newlines one\n\n two\n) +# CHECK: [one +# CHECK: +# CHECK: ] +# CHECK: [two +# CHECK: ] +# string collect returns 0 when it has any output, otherwise 1 +string collect >/dev/null; and echo unexpected success; or echo expected failure +# CHECK: expected failure +echo -n | string collect >/dev/null; and echo unexpected success; or echo expected failure +# CHECK: expected failure +echo | string collect -N >/dev/null; and echo expected success; or echo unexpected failure +# CHECK: expected success +echo | string collect >/dev/null; and echo unexpected success; or echo expected failure +# CHECK: expected failure +string collect a >/dev/null; and echo expected success; or echo unexpected failure +# CHECK: expected success +string collect -N '' >/dev/null; and echo unexpected success; or echo expected failure +# CHECK: expected failure +string collect \n\n >/dev/null; and echo unexpected success; or echo expected failure +# CHECK: expected failure + +# string collect in functions +# This function outputs some newline-separated content, and some +# explicitly un-separated content. +function dualcollect + echo alpha + echo beta + echo gamma\ndelta\nomega | string collect +end +count (dualcollect) +# CHECK: 3