mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 10:17:35 +00:00
Shell: Move to Userland/Shell/
This commit is contained in:
parent
07c7e35372
commit
c4e2fd8123
35 changed files with 11 additions and 9 deletions
55
Userland/Shell/Tests/backgrounding.sh
Normal file
55
Userland/Shell/Tests/backgrounding.sh
Normal file
|
@ -0,0 +1,55 @@
|
|||
#!/bin/Shell
|
||||
|
||||
echo "Not running Shell-backgrounding as it has a high failure rate"
|
||||
exit 0
|
||||
|
||||
setopt --verbose
|
||||
|
||||
fail(msg) {
|
||||
echo FAIL: $msg
|
||||
exit 1
|
||||
}
|
||||
|
||||
last_idx=''
|
||||
block_idx=0
|
||||
block() {
|
||||
block_idx=$(expr 1 + $block_idx)
|
||||
last_idx=$block_idx
|
||||
mkfifo fifo$block_idx
|
||||
cat fifo$block_idx&
|
||||
}
|
||||
|
||||
unblock(idx) {
|
||||
echo unblock $idx > fifo$idx
|
||||
rm -f fifo$idx
|
||||
}
|
||||
|
||||
assert_job_count(count) {
|
||||
ecount=$(jobs | wc -l)
|
||||
shift
|
||||
if test $ecount -ne $count {
|
||||
for $* {
|
||||
unblock $it
|
||||
}
|
||||
fail "expected $ecount == $count"
|
||||
}
|
||||
}
|
||||
|
||||
block
|
||||
i=$last_idx
|
||||
|
||||
assert_job_count 1 $i
|
||||
|
||||
unblock $i
|
||||
wait
|
||||
|
||||
block
|
||||
i=$last_idx
|
||||
block
|
||||
j=$last_idx
|
||||
|
||||
assert_job_count 2 $i $j
|
||||
|
||||
unblock $i
|
||||
unblock $j
|
||||
wait
|
21
Userland/Shell/Tests/brace-exp.sh
Normal file
21
Userland/Shell/Tests/brace-exp.sh
Normal file
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
setopt --verbose
|
||||
|
||||
fail() {
|
||||
echo $*
|
||||
exit 1
|
||||
}
|
||||
|
||||
test "$(echo {a,b,})" = "a b " || fail normal brace expansion with one empty slot
|
||||
test "$(echo {a,,b})" = "a b" || fail normal brace expansion with one empty slot
|
||||
test "$(echo {a,,,b})" = "a b" || fail normal brace expansion with two empty slots
|
||||
test "$(echo {a,b,,})" = "a b " || fail normal brace expansion with two empty slots
|
||||
|
||||
test "$(echo {a..c})" = "a b c" || fail range brace expansion, alpha
|
||||
test "$(echo {0..3})" = "0 1 2 3" || fail range brace expansion, number
|
||||
test "$(echo {😂..😄})" = "😂 😃 😄" || fail range brace expansion, unicode codepoint
|
||||
|
||||
# Make sure that didn't mess with dots and commas in normal barewords
|
||||
test .. = ".." || fail range brace expansion delimiter affects normal barewords
|
||||
test , = "," || fail normal brace expansion delimiter affects normal barewords
|
17
Userland/Shell/Tests/builtin-redir.sh
Normal file
17
Userland/Shell/Tests/builtin-redir.sh
Normal file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
|
||||
rm -rf shell-test
|
||||
mkdir -p shell-test
|
||||
cd shell-test
|
||||
|
||||
time sleep 1 2>timeerr >timeout
|
||||
cat timeout
|
||||
# We cannot be sure about the values, so just assert that they're not empty.
|
||||
test -n "$(cat timeerr)" || echo "Failure: 'time' stderr output not redirected correctly" && exit 1
|
||||
test -e timeout || echo "Failure: 'time' stdout output not redirected correctly" && exit 1
|
||||
|
||||
time ls 2> /dev/null | head > timeout
|
||||
test -n "$(cat timeout)" || echo "Failure: 'time' stdout not piped correctly" && exit 1
|
||||
|
||||
cd ..
|
||||
rm -rf shell-test # TODO: Remove this file at the end once we have `trap'
|
39
Userland/Shell/Tests/control-structure-as-command.sh
Normal file
39
Userland/Shell/Tests/control-structure-as-command.sh
Normal file
|
@ -0,0 +1,39 @@
|
|||
#!/bin/sh
|
||||
|
||||
setopt --verbose
|
||||
|
||||
rm -rf shell-test 2> /dev/null
|
||||
mkdir shell-test
|
||||
cd shell-test
|
||||
|
||||
touch a b c
|
||||
|
||||
# Can we do logical stuff with control structures?
|
||||
ls && for $(seq 1) { echo yes > listing }
|
||||
test "$(cat listing)" = "yes" || echo for cannot appear as second part of '&&' && exit 1
|
||||
rm listing
|
||||
|
||||
# FIXME: This should work!
|
||||
# for $(seq 1) { echo yes > listing } && echo HELLO!
|
||||
# test "$(cat listing)" = "yes" || echo for cannot appear as first part of '&&' && exit 1
|
||||
# rm listing
|
||||
|
||||
# Can we pipe things into and from control structures?
|
||||
ls | if true { cat > listing }
|
||||
test "$(cat listing)" = "a b c" || echo if cannot be correctly redirected to && exit 1
|
||||
rm listing
|
||||
|
||||
ls | for $(seq 1) { cat > listing }
|
||||
test "$(cat listing)" = "a b c" || echo for cannot be correctly redirected to && exit 1
|
||||
rm listing
|
||||
|
||||
for $(seq 4) { echo $it } | cat > listing
|
||||
test "$(cat listing)" = "1 2 3 4" || echo for cannot be correctly redirected from && exit 1
|
||||
rm listing
|
||||
|
||||
if true { echo TRUE! } | cat > listing
|
||||
test "$(cat listing)" = "TRUE!" || echo if cannot be correctly redirected from && exit 1
|
||||
rm listing
|
||||
|
||||
cd ..
|
||||
rm -rf shell-test
|
41
Userland/Shell/Tests/function.sh
Normal file
41
Userland/Shell/Tests/function.sh
Normal file
|
@ -0,0 +1,41 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Syntax ok?
|
||||
fn() { echo $* }
|
||||
|
||||
# Can we invoke that?
|
||||
test "$(fn 1)" = 1 || echo cannot invoke "'fn 1'" && exit 1
|
||||
test "$(fn 1 2)" = "1 2" || echo cannot invoke "'fn 1 2'" && exit 1
|
||||
|
||||
# With explicit argument names?
|
||||
fn(a) { echo $a }
|
||||
|
||||
# Can we invoke that?
|
||||
test "$(fn 1)" = 1 || echo cannot invoke "'fn 1'" with explicit names && exit 1
|
||||
test "$(fn 1 2)" = 1 || echo cannot invoke "'fn 1 2'" with explicit names and extra arguments && exit 1
|
||||
|
||||
# Can it fail?
|
||||
if fn 2>/dev/null {
|
||||
echo "'fn'" with an explicit argument is not failing with not enough args
|
||||
exit 1
|
||||
}
|
||||
|
||||
# $0 in function should be its name
|
||||
fn() { echo $0 }
|
||||
|
||||
test "$(fn)" = fn || echo '$0' in function not equal to its name && exit 1
|
||||
|
||||
# Ensure ARGV does not leak from inner frames.
|
||||
fn() {
|
||||
fn2 1 2 3
|
||||
echo $*
|
||||
}
|
||||
|
||||
fn2() { }
|
||||
|
||||
test "$(fn foobar)" = "foobar" || echo 'Frames are somehow messed up in nested functions' && exit 1
|
||||
|
||||
fn(xfoo) { }
|
||||
xfoo=1
|
||||
fn 2
|
||||
test $xfoo -eq 1 || echo 'Functions overwrite parent scopes' && exit 1
|
50
Userland/Shell/Tests/if.sh
Normal file
50
Userland/Shell/Tests/if.sh
Normal file
|
@ -0,0 +1,50 @@
|
|||
#!/bin/sh
|
||||
|
||||
setopt --verbose
|
||||
|
||||
if test 1 -eq 1 {
|
||||
# Are comments ok?
|
||||
# Basic 'if' structure, empty block.
|
||||
if true {
|
||||
} else {
|
||||
echo "if true runs false branch"
|
||||
exit 2
|
||||
}
|
||||
if false {
|
||||
echo "if false runs true branch"
|
||||
exit 2
|
||||
} else {
|
||||
}
|
||||
|
||||
# Basic 'if' structure, without 'else'
|
||||
if false {
|
||||
echo "Fail: 'if false' runs the branch"
|
||||
exit 2
|
||||
}
|
||||
|
||||
# Extended 'cond' form.
|
||||
if false {
|
||||
echo "Fail: 'if false' with 'else if' runs first branch"
|
||||
exit 2
|
||||
} else if true {
|
||||
} else {
|
||||
echo "Fail: 'if false' with 'else if' runs last branch"
|
||||
exit 2
|
||||
}
|
||||
|
||||
# FIXME: Some form of 'not' would be nice
|
||||
# &&/|| in condition
|
||||
if true || false {
|
||||
} else {
|
||||
echo "Fail: 'if true || false' runs false branch"
|
||||
exit 2
|
||||
}
|
||||
|
||||
if true && false {
|
||||
echo "Fail: 'if true && false' runs true branch"
|
||||
exit 2
|
||||
}
|
||||
} else {
|
||||
echo "Fail: 'if test 1 -eq 1' runs false branch"
|
||||
exit 1
|
||||
}
|
77
Userland/Shell/Tests/loop.sh
Normal file
77
Userland/Shell/Tests/loop.sh
Normal file
|
@ -0,0 +1,77 @@
|
|||
#!/bin/sh
|
||||
|
||||
singlecommand_ok=yes
|
||||
multicommand_ok=yes
|
||||
inlineexec_ok=yes
|
||||
implicit_ok=yes
|
||||
infinite_ok=''
|
||||
break_ok=yes
|
||||
continue_ok=yes
|
||||
break_in_infinite_ok=''
|
||||
|
||||
# Full form
|
||||
# Empty
|
||||
for x in () { }
|
||||
|
||||
# Empty block but nonempty list
|
||||
for x in (1 2 3) { }
|
||||
|
||||
# Single command in block
|
||||
for cmd in ((test 1 = 1) (test 2 = 2)) {
|
||||
$cmd || unset singlecommand_ok
|
||||
}
|
||||
|
||||
# Multiple commands in block
|
||||
for cmd in ((test 1 = 1) (test 2 = 2)) {
|
||||
test -z "$cmd"
|
||||
test -z "$cmd" && unset multicommand_ok
|
||||
|
||||
}
|
||||
|
||||
# $(...) as iterable expression
|
||||
test_file=sh-test-1
|
||||
echo 1 > $test_file
|
||||
echo 2 >> $test_file
|
||||
echo 3 >> $test_file
|
||||
echo 4 >> $test_file
|
||||
lst=()
|
||||
for line in $(cat $test_file) {
|
||||
lst=($lst $line)
|
||||
}
|
||||
test "$lst" = "1 2 3 4" || unset inlineexec_ok
|
||||
rm $test_file
|
||||
|
||||
# Implicit var
|
||||
for ((test 1 = 1) (test 2 = 2)) {
|
||||
$it || unset implicit_ok
|
||||
}
|
||||
|
||||
# Infinite loop
|
||||
loop {
|
||||
infinite_ok=yes
|
||||
break
|
||||
unset break_ok
|
||||
}
|
||||
|
||||
# 'Continue'
|
||||
for (1 2 3) {
|
||||
continue
|
||||
unset continue_ok
|
||||
}
|
||||
|
||||
# 'break' in infinite external loop
|
||||
for $(yes) {
|
||||
break_in_infinite_ok=yes
|
||||
break
|
||||
}
|
||||
|
||||
test $singlecommand_ok || echo Fail: Single command inside for body
|
||||
test $multicommand_ok || echo Fail: Multiple commands inside for body
|
||||
test $inlineexec_ok || echo Fail: Inline Exec
|
||||
test $implicit_ok || echo Fail: implicit iter variable
|
||||
test $infinite_ok || echo Fail: infinite loop
|
||||
test $break_ok || echo Fail: break
|
||||
test $continue_ok || echo Fail: continue
|
||||
test $break_in_infinite_ok || echo Fail: break from external infinite loop
|
||||
|
||||
test "$singlecommand_ok $multicommand_ok $inlineexec_ok $implicit_ok $infinite_ok $break_ok $continue_ok $break_in_infinite_ok" = "yes yes yes yes yes yes yes yes" || exit 1
|
83
Userland/Shell/Tests/match.sh
Normal file
83
Userland/Shell/Tests/match.sh
Normal file
|
@ -0,0 +1,83 @@
|
|||
#!/bin/Shell
|
||||
|
||||
result=no
|
||||
match hello {
|
||||
he* { result=yes }
|
||||
* { result=fail }
|
||||
};
|
||||
|
||||
test "$result" = yes || echo invalid result $result for normal string match, single option && exit 1
|
||||
|
||||
result=no
|
||||
match hello {
|
||||
he* | f* { result=yes }
|
||||
* { result=fail }
|
||||
};
|
||||
|
||||
test "$result" = yes || echo invalid result $result for normal string match, multiple options && exit 1
|
||||
|
||||
result=no
|
||||
match (well hello friends) {
|
||||
(* *) { result=fail }
|
||||
(* * *) { result=yes }
|
||||
* { result=fail }
|
||||
};
|
||||
|
||||
test "$result" = yes || echo invalid result $result for list match && exit 1
|
||||
|
||||
result=no
|
||||
match yes as v {
|
||||
() { result=fail }
|
||||
(*) { result=yes }
|
||||
* { result=$v }
|
||||
};
|
||||
|
||||
test "$result" = yes || echo invalid result $result for match with name && exit 1
|
||||
|
||||
result=no
|
||||
# $(...) is a list, $(echo) should be an empty list, not an empty string
|
||||
match $(echo) {
|
||||
* { result=fail }
|
||||
() { result=yes }
|
||||
};
|
||||
|
||||
test "$result" = yes || echo invalid result $result for list subst match && exit 1
|
||||
|
||||
result=no
|
||||
# "$(...)" is a string, "$(echo)" should be an empty string, not an empty list
|
||||
match "$(echo)" {
|
||||
* { result=yes }
|
||||
() { result=fail }
|
||||
};
|
||||
|
||||
test "$result" = yes || echo invalid result $result for string subst match && exit 1
|
||||
|
||||
match (foo bar) {
|
||||
(f? *) as (x y) {
|
||||
result=fail
|
||||
}
|
||||
(f* b*) as (x y) {
|
||||
if [ "$x" = oo -a "$y" = ar ] {
|
||||
result=yes
|
||||
} else {
|
||||
result=fail
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "$result" = yes || echo invalid result $result for subst match with name && exit 1
|
||||
|
||||
match (foo bar baz) {
|
||||
(f? * *z) as (x y z) {
|
||||
result=fail
|
||||
}
|
||||
(f* b* *z) as (x y z) {
|
||||
if [ "$x" = oo -a "$y" = ar -a "$z" = ba ] {
|
||||
result=yes
|
||||
} else {
|
||||
result=fail
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "$result" = yes || echo invalid result $result for subst match with name 2 && exit 1
|
13
Userland/Shell/Tests/sigpipe.sh
Normal file
13
Userland/Shell/Tests/sigpipe.sh
Normal file
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
|
||||
# `head -n 1` should close stdout of the `Shell -c` command, which means the
|
||||
# second echo should exit unsuccessfully and sigpipe.sh.out should not be
|
||||
# created.
|
||||
rm -f sigpipe.sh.out
|
||||
|
||||
{ echo foo && echo bar && echo baz > sigpipe.sh.out } | head -n 1 > /dev/null
|
||||
|
||||
# Failing commands don't make the test fail, just an explicit `exit 1` does.
|
||||
# So the test only fails if sigpipe.sh.out exists (since then `exit 1` runs),
|
||||
# not if the `test` statement returns false.
|
||||
test -e sigpipe.sh.out && exit 1
|
15
Userland/Shell/Tests/special-vars.sh
Normal file
15
Userland/Shell/Tests/special-vars.sh
Normal file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
test "$*" = "" || echo "Fail: Argv list not empty" && exit 1
|
||||
test "$#" -eq 0 || echo "Fail: Argv list empty but count non-zero" && exit 1
|
||||
test "$ARGV" = "$*" || echo "Fail: \$ARGV not equal to \$*" && exit 1
|
||||
|
||||
ARGV=(1 2 3)
|
||||
test "$#" -eq 3 || echo "Fail: Assignment to ARGV does not affect \$#" && exit 1
|
||||
test "$*" = "1 2 3" || echo "Fail: Assignment to ARGV does not affect \$*" && exit 1
|
||||
|
||||
shift
|
||||
test "$*" = "2 3" || echo "Fail: 'shift' does not work correctly" && exit 1
|
||||
|
||||
shift 2
|
||||
test "$*" = "" || echo "Fail: 'shift 2' does not work correctly" && exit 1
|
28
Userland/Shell/Tests/subshell.sh
Normal file
28
Userland/Shell/Tests/subshell.sh
Normal file
|
@ -0,0 +1,28 @@
|
|||
#/bin/sh
|
||||
|
||||
setopt --verbose
|
||||
|
||||
rm -rf shell-test
|
||||
mkdir shell-test
|
||||
cd shell-test
|
||||
|
||||
# Simple sequence (grouping)
|
||||
{ echo test > testfile }
|
||||
test "$(cat testfile)" = "test" || echo cannot write to file in subshell && exit 1
|
||||
|
||||
# Simple sequence - many commands
|
||||
{ echo test1 > testfile; echo test2 > testfile }
|
||||
test "$(cat testfile)" = "test2" || echo cannot write to file in subshell 2 && exit 1
|
||||
|
||||
|
||||
# Does it exit with the last exit code?
|
||||
{ test -z "a" }
|
||||
exitcode=$?
|
||||
test "$exitcode" -eq 1 || echo exits with $exitcode when it should exit with 1 && exit 1
|
||||
|
||||
{ test -z "a" || echo test }
|
||||
exitcode=$?
|
||||
test "$exitcode" -eq 0 || echo exits with $exitcode when it should exit with 0 && exit 1
|
||||
|
||||
cd ..
|
||||
rm -rf shell-test
|
92
Userland/Shell/Tests/valid.sh
Normal file
92
Userland/Shell/Tests/valid.sh
Normal file
|
@ -0,0 +1,92 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Are comments ignored?
|
||||
# Sanity check: can we do && and || ?
|
||||
true || exit 2
|
||||
false
|
||||
|
||||
# Apply some useful aliases
|
||||
fail() {
|
||||
echo $*
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Can we chain &&'s?
|
||||
false && exit 2 && fail "can't chain &&'s"
|
||||
|
||||
# Proper precedence between &&'s and ||'s
|
||||
false && exit 2 || true && false && fail Invalid precedence between '&&' and '||'
|
||||
|
||||
|
||||
# Sanity check: can we pass arguments to 'test'?
|
||||
test yes = yes || exit 2
|
||||
|
||||
# Sanity check: can we use $(command)?
|
||||
test "$(echo yes)" = yes || exit 2
|
||||
# Redirections.
|
||||
test -z "$(echo foo > /dev/null)" || fail direct path redirection
|
||||
test -z "$(echo foo 2> /dev/null 1>&2)" || fail indirect redirection
|
||||
test -n "$(echo foo 2> /dev/null)" || fail fds interfere with each other
|
||||
|
||||
# Argument unpack
|
||||
test "$(echo (yes))" = yes || fail arguments inside bare lists
|
||||
test "$(echo (no)() yes)" = yes || fail arguments inside juxtaposition: empty
|
||||
test "$(echo (y)(es))" = yes || fail arguments inside juxtaposition: list
|
||||
test "$(echo "y"es)" = yes || fail arguments inside juxtaposition: string
|
||||
|
||||
# String substitution
|
||||
foo=yes
|
||||
test "$(echo $foo)" = yes || fail simple string var lookup
|
||||
test "$(echo "$foo")" = yes || fail stringified string var lookup
|
||||
|
||||
# List substitution
|
||||
foo=(yes)
|
||||
# Static lookup, as list
|
||||
test "$(echo $foo)" = yes || fail simple list var lookup
|
||||
# Static lookup, stringified
|
||||
test "$(echo "$foo")" = yes || fail stringified list var lookup
|
||||
# Dynamic lookup through static expression
|
||||
test "$(echo $'foo')" = yes || fail dynamic lookup through static exp
|
||||
# Dynamic lookup through dynamic expression
|
||||
ref_to_foo=foo
|
||||
test "$(echo $"$ref_to_foo")" = yes || fail dynamic lookup through dynamic exp
|
||||
|
||||
# More redirections
|
||||
echo test > /tmp/sh-test
|
||||
test "$(cat /tmp/sh-test)" = test || fail simple path redirect
|
||||
rm /tmp/sh-test
|
||||
|
||||
# 'brace' expansions
|
||||
test "$(echo x(yes no))" = "xyes xno" || fail simple juxtaposition expansion
|
||||
test "$(echo (y n)(es o))" = "yes yo nes no" || fail list-list juxtaposition expansion
|
||||
test "$(echo ()(foo bar baz))" = "" || fail empty expansion
|
||||
|
||||
# Variables inside commands
|
||||
to_devnull=(>/dev/null)
|
||||
test "$(echo hewwo $to_devnull)" = "" || fail variable containing simple command
|
||||
|
||||
word_count=(() | wc -w)
|
||||
test "$(echo well hello friends $word_count)" -eq 3 || fail variable containing pipeline
|
||||
|
||||
# Globs
|
||||
mkdir sh-test
|
||||
pushd sh-test
|
||||
touch (a b c)(d e f)
|
||||
test "$(echo a*)" = "ad ae af" || fail '*' glob expansion
|
||||
test "$(echo a?)" = "ad ae af" || fail '?' glob expansion
|
||||
|
||||
glob_in_var='*'
|
||||
test "$(echo $glob_in_var)" = '*' || fail substituted string acts as glob
|
||||
|
||||
test "$(echo (a*))" = "ad ae af" || fail globs in lists resolve wrong
|
||||
test "$(echo x(a*))" = "xad xae xaf" || fail globs in lists do not resolve to lists
|
||||
test "$(echo "foo"a*)" = "fooad fooae fooaf" || fail globs join to dquoted strings
|
||||
popd
|
||||
rm -fr sh-test
|
||||
|
||||
# Setopt
|
||||
setopt --inline_exec_keep_empty_segments
|
||||
test "$(echo -n "a\n\nb")" = "a b" || fail inline_exec_keep_empty_segments has no effect
|
||||
|
||||
setopt --no_inline_exec_keep_empty_segments
|
||||
test "$(echo -n "a\n\nb")" = "a b" || fail cannot unset inline_exec_keep_empty_segments
|
Loading…
Add table
Add a link
Reference in a new issue