mirror of
https://codeberg.org/la-chouette/minishell.git
synced 2025-12-06 07:28:09 +01:00
1613 lines
26 KiB
Bash
Executable file
1613 lines
26 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
set -uo pipefail
|
|
|
|
declare -i FAILED=0
|
|
declare -i SUCCEDED=0
|
|
declare -i TODO=0
|
|
declare -i RAN=0
|
|
NAME=""
|
|
EXTRAENV=""
|
|
MINISHELL=$PWD/minishell
|
|
|
|
report_header()
|
|
{
|
|
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++"
|
|
echo $NAME
|
|
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++"
|
|
echo "++++ test input:"
|
|
}
|
|
|
|
failed_report()
|
|
{
|
|
NAME="failed: $NAME"
|
|
report_header
|
|
cat /tmp/input.minishell
|
|
echo "++++ Got output:"
|
|
cat /tmp/got.minishell
|
|
echo "++++ But expected:"
|
|
cat /tmp/expected.minishell
|
|
echo "++++ Diff:"
|
|
echo "(red is got, green is expected)"
|
|
diff --color=always /tmp/got.minishell /tmp/expected.minishell
|
|
echo "++++ Run this to debug:"
|
|
cat < /tmp/input.minishell > /tmp/input.failed.$FAILED.minishell
|
|
echo "cat /tmp/input.failed.$FAILED.minishell | ./minishell"
|
|
}
|
|
|
|
todo_report()
|
|
{
|
|
NAME="todo: $NAME"
|
|
report_header
|
|
cat /tmp/input.minishell
|
|
echo "++++ Got output:"
|
|
cat /tmp/got.minishell
|
|
echo "++++ But test was marked as todo."
|
|
echo "++++ Run this to debug:"
|
|
cat < /tmp/input.minishell > /tmp/input.todo.$TODO.minishell
|
|
echo "cat /tmp/input.todo.$TODO.minishell | ./minishell"
|
|
}
|
|
|
|
failed()
|
|
{
|
|
echo -n "F"
|
|
FAILED+=1
|
|
failed_report >> report.txt
|
|
}
|
|
|
|
succeded()
|
|
{
|
|
echo -n "."
|
|
SUCCEDED+=1
|
|
}
|
|
|
|
assert()
|
|
{
|
|
diff -q /tmp/got.minishell /tmp/expected.minishell > /dev/null && succeded || failed
|
|
}
|
|
|
|
when_run()
|
|
{
|
|
cat > /tmp/input.minishell
|
|
rm -rf /tmp/dir.minishell
|
|
mkdir -p /tmp/dir.minishell
|
|
pushd /tmp/dir.minishell > /dev/null
|
|
env $EXTRAENV $MINISHELL &> /tmp/got.minishell < /tmp/input.minishell
|
|
popd > /dev/null
|
|
EXTRAENV=""
|
|
RAN+=1
|
|
NAME=$1
|
|
}
|
|
|
|
expecting()
|
|
{
|
|
cat > /tmp/expected.minishell
|
|
assert
|
|
}
|
|
|
|
todo()
|
|
{
|
|
echo -n "t"
|
|
TODO+=1
|
|
todo_report >> report.txt
|
|
}
|
|
|
|
setup()
|
|
{
|
|
rm -f report.txt
|
|
}
|
|
|
|
finalize()
|
|
{
|
|
echo
|
|
echo "$RAN tests ran. $SUCCEDED succeded, $FAILED failed, $TODO todo."
|
|
if [ $SUCCEDED -eq $RAN ];
|
|
then
|
|
echo "All integration tests passed!"
|
|
else
|
|
echo "cat report.txt"
|
|
echo "to see details of failed/todo tests"
|
|
fi
|
|
exit $FAILED
|
|
}
|
|
|
|
setup
|
|
|
|
when_run <<EOF "empty inputs returns nothing"
|
|
|
|
EOF
|
|
expecting <<EOF
|
|
EOF
|
|
|
|
when_run <<EOF "only spaces returns nothing"
|
|
|
|
EOF
|
|
expecting <<EOF
|
|
EOF
|
|
|
|
when_run <<EOF "only tabs returns nothing"
|
|
|
|
EOF
|
|
expecting <<EOF
|
|
EOF
|
|
|
|
when_run <<EOF "/ is a directory"
|
|
/
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: /: Is a directory
|
|
126
|
|
EOF
|
|
|
|
when_run <<EOF "// is a directory"
|
|
//
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: //: Is a directory
|
|
126
|
|
EOF
|
|
|
|
when_run <<EOF "/. is a directory"
|
|
/.
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: /.: Is a directory
|
|
126
|
|
EOF
|
|
|
|
when_run <<EOF "/./../../../../.. is a directory"
|
|
/./../../../../..
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: /./../../../../..: Is a directory
|
|
126
|
|
EOF
|
|
|
|
when_run <<EOF "//////// is a directory"
|
|
////////
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: ////////: Is a directory
|
|
126
|
|
EOF
|
|
|
|
when_run <<EOF "simple commands are run"
|
|
echo no files:
|
|
ls -a
|
|
touch t
|
|
echo single file:
|
|
ls -a
|
|
EOF
|
|
expecting <<EOF
|
|
no files:
|
|
.
|
|
..
|
|
single file:
|
|
.
|
|
..
|
|
t
|
|
EOF
|
|
|
|
when_run <<EOF "unknown commands"
|
|
qwertyuiop
|
|
echo \$?
|
|
poiuytrewq
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: qwertyuiop: command not found
|
|
127
|
|
minishell: poiuytrewq: command not found
|
|
127
|
|
EOF
|
|
|
|
when_run <<EOF "pwd works"
|
|
pwd
|
|
pwd hello
|
|
pwd hello there
|
|
pwd -O
|
|
pwd -O hello
|
|
pwd there -O hello
|
|
EOF
|
|
expecting <<EOF
|
|
/tmp/dir.minishell
|
|
/tmp/dir.minishell
|
|
/tmp/dir.minishell
|
|
/tmp/dir.minishell
|
|
/tmp/dir.minishell
|
|
/tmp/dir.minishell
|
|
EOF
|
|
|
|
when_run <<EOF "pwd works in any directory, can cd to absolute paths"
|
|
mkdir test
|
|
cd /tmp/dir.minishell/test
|
|
pwd
|
|
mkdir hi
|
|
cd /tmp/dir.minishell/test/hi
|
|
pwd
|
|
cd /tmp/dir.minishell/test
|
|
pwd
|
|
EOF
|
|
expecting <<EOF
|
|
/tmp/dir.minishell/test
|
|
/tmp/dir.minishell/test/hi
|
|
/tmp/dir.minishell/test
|
|
EOF
|
|
|
|
when_run <<EOF "cd will print error when going into a directory with no access"
|
|
mkdir test
|
|
chmod -x test
|
|
cd test
|
|
echo \$?
|
|
pwd
|
|
chmod +x test
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: cd: test: Permission denied
|
|
1
|
|
/tmp/dir.minishell
|
|
EOF
|
|
|
|
EXTRAENV="HOME=/tmp/dir.minishell/fakehome"
|
|
when_run <<EOF "cd with no arguments goes to \$HOME"
|
|
mkdir fakehome
|
|
cd
|
|
pwd
|
|
EOF
|
|
expecting <<EOF
|
|
/tmp/dir.minishell/fakehome
|
|
EOF
|
|
|
|
EXTRAENV="-u HOME"
|
|
when_run <<EOF "cd with no arguments, with HOME unset, prints error message and does nothing"
|
|
cd
|
|
pwd
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: cd: HOME not set
|
|
/tmp/dir.minishell
|
|
EOF
|
|
|
|
when_run <<EOF "cd with empty argument"
|
|
cd ""
|
|
pwd
|
|
EOF
|
|
expecting <<EOF
|
|
/tmp/dir.minishell
|
|
EOF
|
|
|
|
EXTRAENV="HOME="
|
|
when_run <<EOF "cd with no arguments and HOME equal to empty string"
|
|
cd
|
|
pwd
|
|
EOF
|
|
expecting <<EOF
|
|
/tmp/dir.minishell
|
|
EOF
|
|
|
|
when_run <<EOF "cd with simple relative paths"
|
|
mkdir test
|
|
cd test
|
|
pwd
|
|
cd ..
|
|
pwd
|
|
EOF
|
|
expecting <<EOF
|
|
/tmp/dir.minishell/test
|
|
/tmp/dir.minishell
|
|
EOF
|
|
|
|
when_run <<EOF "cd with complicated relative paths"
|
|
mkdir -p test/hello/there
|
|
mkdir -p test/hi/there
|
|
mkdir -p test/hello/world
|
|
cd test/hello/there
|
|
pwd
|
|
cd ../../hi
|
|
pwd
|
|
cd ../hi/../hi/../hi/../hi
|
|
pwd
|
|
cd ../hi/../hi/../hi/../hi/../../test/hello/world
|
|
pwd
|
|
EOF
|
|
expecting <<EOF
|
|
/tmp/dir.minishell/test/hello/there
|
|
/tmp/dir.minishell/test/hi
|
|
/tmp/dir.minishell/test/hi
|
|
/tmp/dir.minishell/test/hello/world
|
|
EOF
|
|
|
|
when_run <<EOF "cd print error when given too many arguments"
|
|
cd hello there
|
|
pwd
|
|
cd . ..
|
|
pwd
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: cd: too many arguments
|
|
/tmp/dir.minishell
|
|
minishell: cd: too many arguments
|
|
/tmp/dir.minishell
|
|
EOF
|
|
|
|
EXTRAENV="VAR=value hello=bye"
|
|
when_run <<"EOF" "variable substitution works"
|
|
echo $VAR $hello
|
|
EOF
|
|
expecting <<EOF
|
|
value bye
|
|
EOF
|
|
|
|
when_run <<"EOF" "export works with single variable"
|
|
export var=testvalue
|
|
echo var=$var
|
|
export var="Another Test Value"
|
|
echo var=$var
|
|
EOF
|
|
expecting <<EOF
|
|
var=testvalue
|
|
var=Another Test Value
|
|
EOF
|
|
|
|
when_run <<"EOF" "export works with multiple variables"
|
|
export name="Sir Reginald Pikedevant" greeting="Well met," counter=27 "question=How many fingers am I holding up?"
|
|
echo $greeting $name
|
|
echo $question
|
|
echo Umm, $counter?
|
|
EOF
|
|
expecting <<EOF
|
|
Well met, Sir Reginald Pikedevant
|
|
How many fingers am I holding up?
|
|
Umm, 27?
|
|
EOF
|
|
|
|
EXTRAENV=-i
|
|
when_run <<EOF "export with strange inputs"
|
|
export ENV=$(which env)
|
|
export PATH=$PATH
|
|
export var
|
|
echo status=\$?
|
|
echo var=[\$var]
|
|
export blue=
|
|
echo status=\$?
|
|
echo blue=[\$blue]
|
|
\$ENV -u PATH -u ENV env
|
|
EOF
|
|
expecting <<EOF
|
|
status=0
|
|
var=[]
|
|
status=0
|
|
blue=[]
|
|
blue=
|
|
EOF
|
|
|
|
when_run <<"EOF" "export with invalid identifiers"
|
|
export = 123=456 hello=hi 456=789
|
|
echo status=$?
|
|
echo hello=$hello
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: export: `=': not a valid identifier
|
|
minishell: export: `123=456': not a valid identifier
|
|
minishell: export: `456=789': not a valid identifier
|
|
status=1
|
|
hello=hi
|
|
EOF
|
|
|
|
when_run <<EOF "empty command is not found"
|
|
""
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: : command not found
|
|
EOF
|
|
|
|
when_run <<EOF "exit status of last command is preserved"
|
|
echo \$?
|
|
ls nonexist
|
|
echo \$?
|
|
echo hi
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
0
|
|
ls: cannot access 'nonexist': No such file or directory
|
|
2
|
|
hi
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "exit works"
|
|
$MINISHELL
|
|
exit
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "exit works with custom exit code"
|
|
$MINISHELL
|
|
exit 99
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
99
|
|
EOF
|
|
|
|
when_run <<EOF "exit works with extreme exit codes"
|
|
$MINISHELL
|
|
exit 1234567890
|
|
echo \$?
|
|
$MINISHELL
|
|
exit -1234567890
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
210
|
|
46
|
|
EOF
|
|
|
|
when_run <<EOF "exit works with nan input"
|
|
$MINISHELL
|
|
exit not-a-number
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: exit: not-a-number: numeric argument required
|
|
2
|
|
EOF
|
|
|
|
when_run <<EOF "exit works with too many arguments"
|
|
$MINISHELL
|
|
exit 1 2
|
|
echo \$?
|
|
exit
|
|
echo \$?
|
|
$MINISHELL
|
|
exit not-a-number 3
|
|
echo \$?
|
|
$MINISHELL
|
|
exit 3 not-a-number
|
|
echo \$?
|
|
exit
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: exit: too many arguments
|
|
1
|
|
0
|
|
minishell: exit: not-a-number: numeric argument required
|
|
2
|
|
minishell: exit: too many arguments
|
|
1
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "echo basic tests"
|
|
echo hello world
|
|
echo "Hello World"
|
|
EOF
|
|
expecting <<EOF
|
|
hello world
|
|
Hello World
|
|
EOF
|
|
|
|
when_run <<EOF "echo -n tests"
|
|
echo -n hello world
|
|
echo
|
|
echo -nnnnnnnnnnnnnnnn hello world
|
|
echo
|
|
echo -nnnntnnnnnnnnnnnn hello world
|
|
echo hi -n hello
|
|
echo -n -n -nnn -n goodbye
|
|
echo
|
|
EOF
|
|
expecting <<EOF
|
|
hello world
|
|
hello world
|
|
-nnnntnnnnnnnnnnnn hello world
|
|
hi -n hello
|
|
goodbye
|
|
EOF
|
|
|
|
when_run <<EOF "echo - n"
|
|
echo - n hi
|
|
EOF
|
|
expecting <<EOF
|
|
- n hi
|
|
EOF
|
|
|
|
EXTRAENV="-i"
|
|
when_run <<EOF "env works"
|
|
echo this should be empty:
|
|
env
|
|
echo
|
|
export var=VALUE hi=hello bye=goodbye
|
|
echo this contains some values
|
|
env
|
|
unset var bye
|
|
echo some vars were unset
|
|
env
|
|
EOF
|
|
expecting <<EOF
|
|
this should be empty:
|
|
|
|
this contains some values
|
|
var=VALUE
|
|
hi=hello
|
|
bye=goodbye
|
|
some vars were unset
|
|
hi=hello
|
|
EOF
|
|
|
|
EXTRAENV="-i"
|
|
when_run <<EOF "env ignores additional arguments"
|
|
export var=VALUE hi=hello bye=goodbye
|
|
env -i name="The Other" ls
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: env: ignoring arguments
|
|
var=VALUE
|
|
hi=hello
|
|
bye=goodbye
|
|
EOF
|
|
|
|
when_run <<EOF "echo field splitting tests"
|
|
export val=" hi there "
|
|
echo -\$val-
|
|
echo -"\$val"-
|
|
export val="hello there"
|
|
echo -\$val-
|
|
echo -"\$val"-
|
|
EOF
|
|
expecting <<EOF
|
|
- hi there -
|
|
- hi there -
|
|
-hello there-
|
|
-hello there-
|
|
EOF
|
|
|
|
when_run <<EOF "wildcards"
|
|
echo *
|
|
touch hi there hello
|
|
echo *
|
|
echo h*
|
|
EOF
|
|
expecting <<EOF
|
|
*
|
|
hello hi there
|
|
hello hi
|
|
EOF
|
|
|
|
when_run <<EOF "quoted parentheses are not operators"
|
|
.debug
|
|
.exec
|
|
echo unclosed '('
|
|
EOF
|
|
expecting <<EOF
|
|
[dbg: 1]
|
|
[exec: 0]
|
|
parsed command
|
|
╰─ t_cmd
|
|
├─ t_cmd_type = FT_SIMPLE
|
|
├─ flags = 0
|
|
├─ line = 0
|
|
╰─ value
|
|
╰─ t_simple_cmd
|
|
├─ line = 0
|
|
├─ t_wordlist
|
|
│ ├─ t_worddesc
|
|
│ │ ├─ word = [echo]
|
|
│ │ ├─ marker = [ ]
|
|
│ │ ├─ flags = 0
|
|
│ │ ╰─ t_token_type = WORD_TOKEN
|
|
│ ├─ t_worddesc
|
|
│ │ ├─ word = [unclosed]
|
|
│ │ ├─ marker = [ ]
|
|
│ │ ├─ flags = 0
|
|
│ │ ╰─ t_token_type = WORD_TOKEN
|
|
│ ╰─ t_worddesc
|
|
│ ├─ word = ['(']
|
|
│ ├─ marker = [ ' ]
|
|
│ ├─ flags = 2
|
|
│ ╰─ t_token_type = WORD_TOKEN
|
|
╰─ redirections = (empty redir list)
|
|
EOF
|
|
|
|
when_run <<EOF "multiple >"
|
|
> > > >
|
|
echo \$?
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `>'
|
|
2
|
|
EOF
|
|
|
|
when_run <<EOF "single pipe char"
|
|
|
|
|
echo \$?
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `|'
|
|
2
|
|
EOF
|
|
|
|
when_run <<EOF "unclosed cmdgroup"
|
|
(echo hi
|
|
echo \$?
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `newline'
|
|
2
|
|
EOF
|
|
|
|
when_run <<EOF "basic pipe, second command does not accept input"
|
|
echo \$? | echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "basic pipe"
|
|
echo hi | cat
|
|
EOF
|
|
expecting <<EOF
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "variable substitution with no valid name is not substitued"
|
|
echo \$=
|
|
echo \$:
|
|
EOF
|
|
expecting <<EOF
|
|
$=
|
|
$:
|
|
EOF
|
|
|
|
when_run <<EOF "variable substitution with a lone dollar sign"
|
|
echo " $ " | cat -e
|
|
EOF
|
|
expecting <<EOF
|
|
$ $
|
|
EOF
|
|
|
|
when_run <<EOF "handling of positional arguments"
|
|
echo \$9HOME
|
|
EOF
|
|
expecting <<EOF
|
|
\$9HOME
|
|
EOF
|
|
|
|
when_run <<EOF "invalid redirection in echo"
|
|
echo < >
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `>'
|
|
EOF
|
|
|
|
when_run <<EOF "here_doc"
|
|
cat << EOT
|
|
hello!
|
|
EOT
|
|
EOF
|
|
expecting <<EOF
|
|
hello!
|
|
EOF
|
|
|
|
when_run <<EOF "double pipe after echo"
|
|
echo | |
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `|'
|
|
EOF
|
|
|
|
when_run <<EOF "empty variables are removed"
|
|
""
|
|
\$EMPTY_NO_VALUE
|
|
echo not \$EMPTY_NO_VALUE printed
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: : command not found
|
|
not printed
|
|
EOF
|
|
|
|
when_run <<EOF "absolute path to command which does not exist"
|
|
/cmd/does/not/exist
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: /cmd/does/not/exist: No such file or directory
|
|
127
|
|
EOF
|
|
|
|
EXTRAENV="HOME=/home/fakeuser"
|
|
when_run <<EOF "?\$HOME"
|
|
?\$HOME
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: ?/home/fakeuser: No such file or directory
|
|
127
|
|
EOF
|
|
|
|
when_run <<"EOF" "export with no arguments"
|
|
export
|
|
export $DONTEXIT
|
|
export $NOSUCHVAR $DONTEXIST
|
|
EOF
|
|
expecting <<EOF
|
|
EOF
|
|
|
|
when_run <<EOF "env with grep"
|
|
env | grep HOME
|
|
EOF
|
|
expecting <<EOF
|
|
$(env | grep HOME)
|
|
EOF
|
|
|
|
when_run <<EOF "export with empty arguments"
|
|
export "" ""
|
|
echo \$?
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: export: `': not a valid identifier
|
|
minishell: export: `': not a valid identifier
|
|
1
|
|
EOF
|
|
|
|
when_run <<EOF "export with invalid identifiers"
|
|
export T%=T T$=T 0=T 1=T \$?=T TE+S=T
|
|
echo \$?
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: export: `T%=T': not a valid identifier
|
|
minishell: export: `T$=T': not a valid identifier
|
|
minishell: export: `0=T': not a valid identifier
|
|
minishell: export: `1=T': not a valid identifier
|
|
minishell: export: `0=T': not a valid identifier
|
|
minishell: export: `TE+S=T': not a valid identifier
|
|
1
|
|
EOF
|
|
|
|
when_run <<"EOF" "export with identifiers containing numbers"
|
|
export VAR1=this VAR2=should VAR3=work VAR4=no VAR5="problem!"
|
|
echo $VAR1 $VAR2 $VAR3 $VAR4 $VAR5
|
|
EOF
|
|
expecting <<EOF
|
|
this should work no problem!
|
|
EOF
|
|
|
|
when_run <<"EOF" "export with identifiers containing and starting with underscore"
|
|
export __var__=__value__ _snake_case_variable_names_=Pascal_Snake_Case
|
|
echo $__var__ $_snake_case_variable_names_
|
|
EOF
|
|
expecting <<EOF
|
|
__value__ Pascal_Snake_Case
|
|
EOF
|
|
|
|
when_run <<"EOF" "export with identifiers containing dashes"
|
|
export -VAR=value lisp-case="(defun botsbuildbots () (botsbuildbots))" --option-case=--no-preserve-root
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: export: `-VAR=value': not a valid identifier
|
|
minishell: export: `lisp-case=(defun botsbuildbots () (botsbuildbots))': not a valid identifier
|
|
minishell: export: `--option-case=--no-preserve-root': not a valid identifier
|
|
EOF
|
|
|
|
when_run <<"EOF" "export with variable substitution in arguments"
|
|
export type=nuclear phrase="the $type option"
|
|
echo $phrase
|
|
export phrase="the $type option" $type="the $type option"
|
|
echo $phrase
|
|
echo $nuclear
|
|
EOF
|
|
expecting <<EOF
|
|
the option
|
|
the nuclear option
|
|
the nuclear option
|
|
EOF
|
|
|
|
when_run <<"EOF" "redirect with expansion in target"
|
|
export target=outfile
|
|
echo -n "hello " > $target
|
|
echo there >> $target
|
|
ls
|
|
cat < $target
|
|
EOF
|
|
expecting <<EOF
|
|
outfile
|
|
hello there
|
|
EOF
|
|
|
|
when_run <<"EOF" "ambiguous redirect"
|
|
export target="outfile1 outfile2"
|
|
echo hello > $target
|
|
echo $?
|
|
echo hi >> $target
|
|
echo $?
|
|
cat < $target
|
|
echo $?
|
|
ls
|
|
echo $?
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: $target: ambiguous redirect
|
|
1
|
|
minishell: $target: ambiguous redirect
|
|
1
|
|
minishell: $target: ambiguous redirect
|
|
1
|
|
0
|
|
EOF
|
|
|
|
when_run <<"EOF" "wildcard expansion in redirect"
|
|
touch "microsoft windows"
|
|
echo hello > *m*cros*ft*
|
|
echo $?
|
|
echo goodbye >> *w*nd*ws*
|
|
echo $?
|
|
ls
|
|
cat < *soft*
|
|
echo $?
|
|
EOF
|
|
expecting <<EOF
|
|
0
|
|
0
|
|
microsoft windows
|
|
hello
|
|
goodbye
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "correct parsing and debugging of multiple redirections"
|
|
.debug
|
|
.exec
|
|
echo hello > outfile > outfile
|
|
EOF
|
|
expecting <<EOF
|
|
[dbg: 1]
|
|
[exec: 0]
|
|
parsed command
|
|
╰─ t_cmd
|
|
├─ t_cmd_type = FT_SIMPLE
|
|
├─ flags = 0
|
|
├─ line = 0
|
|
╰─ value
|
|
╰─ t_simple_cmd
|
|
├─ line = 0
|
|
├─ t_wordlist
|
|
│ ├─ t_worddesc
|
|
│ │ ├─ word = [echo]
|
|
│ │ ├─ marker = [ ]
|
|
│ │ ├─ flags = 0
|
|
│ │ ╰─ t_token_type = WORD_TOKEN
|
|
│ ╰─ t_worddesc
|
|
│ ├─ word = [hello]
|
|
│ ├─ marker = [ ]
|
|
│ ├─ flags = 0
|
|
│ ╰─ t_token_type = WORD_TOKEN
|
|
╰─ redirections
|
|
├─ redirections[0]
|
|
│ ╰─ t_redirect
|
|
│ ├─ redir_type = FT_OUTPUT_TRUNC_REDIR
|
|
│ ├─ source = 1
|
|
│ ├─ open_flags = 577
|
|
│ ├─ c_flags = 420
|
|
│ ├─ t_redirectee
|
|
│ │ ╰─ t_worddesc
|
|
│ │ ├─ word = [outfile]
|
|
│ │ ├─ marker = [ ]
|
|
│ │ ├─ flags = 0
|
|
│ │ ╰─ t_token_type = WORD_TOKEN
|
|
│ ├─ c_flags = [(null)]
|
|
│ ╰─ here_doc_eof = [(null)]
|
|
╰─ redirections[1]
|
|
╰─ t_redirect
|
|
├─ redir_type = FT_OUTPUT_TRUNC_REDIR
|
|
├─ source = 1
|
|
├─ open_flags = 577
|
|
├─ c_flags = 420
|
|
├─ t_redirectee
|
|
│ ╰─ t_worddesc
|
|
│ ├─ word = [outfile]
|
|
│ ├─ marker = [ ]
|
|
│ ├─ flags = 0
|
|
│ ╰─ t_token_type = WORD_TOKEN
|
|
├─ c_flags = [(null)]
|
|
╰─ here_doc_eof = [(null)]
|
|
EOF
|
|
|
|
when_run <<EOF "correctly handle multiple redirections"
|
|
echo hello > outfile > outfile
|
|
cat outfile
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
EOF
|
|
|
|
when_run <<"EOF" "unquoting behaviour"
|
|
export target="'outfile'"
|
|
echo $target
|
|
EOF
|
|
expecting <<EOF
|
|
'outfile'
|
|
EOF
|
|
|
|
when_run <<"EOF" "quote removal in redirect"
|
|
echo 42 > "the answer"
|
|
echo $?
|
|
ls
|
|
echo 42 >> "the answer"
|
|
echo $?
|
|
cat < "the answer"
|
|
echo $?
|
|
EOF
|
|
expecting <<EOF
|
|
0
|
|
the answer
|
|
0
|
|
42
|
|
42
|
|
0
|
|
EOF
|
|
|
|
when_run <<"EOF" "no glob expansion in variable expansions"
|
|
touch "the answer"
|
|
export target="the*"
|
|
echo $target
|
|
echo hi > $target
|
|
echo hello >> $target
|
|
cat < $target
|
|
ls
|
|
EOF
|
|
expecting <<"EOF"
|
|
the answer
|
|
hi
|
|
hello
|
|
the answer
|
|
EOF
|
|
|
|
when_run <<EOF "output to file with no access"
|
|
echo hello > /root/outfile
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: /root/outfile: Permission denied
|
|
1
|
|
EOF
|
|
|
|
when_run <<"EOF" "print error when unclosed quotes"
|
|
echo " kjlhgsaf
|
|
echo $?
|
|
echo ' HGIFUY
|
|
echo $?
|
|
echo '"HGIFUY"
|
|
echo $?
|
|
echo '"HGIFUY
|
|
echo $?
|
|
echo "'HGIFUY
|
|
echo $?
|
|
echo "' HGI'FUY
|
|
echo $?
|
|
echo boubou
|
|
echo $?
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: unexpected EOF while looking for matching `"'
|
|
2
|
|
minishell: unexpected EOF while looking for matching `''
|
|
2
|
|
minishell: unexpected EOF while looking for matching `''
|
|
2
|
|
minishell: unexpected EOF while looking for matching `''
|
|
2
|
|
minishell: unexpected EOF while looking for matching `"'
|
|
2
|
|
minishell: unexpected EOF while looking for matching `"'
|
|
2
|
|
boubou
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "PATH unset with command in current dir"
|
|
touch cat
|
|
unset PATH
|
|
cat
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: cat: Permission denied
|
|
126
|
|
EOF
|
|
|
|
when_run <<EOF "unclosed ("
|
|
(echo unclosed paren
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `newline'
|
|
EOF
|
|
|
|
when_run <<"EOF" "unclosed ( in export"
|
|
export VAR=bon(jour
|
|
echo $?
|
|
echo var=$VAR
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `('
|
|
2
|
|
var=
|
|
EOF
|
|
|
|
when_run <<"EOF" "unmatched )"
|
|
echo unclosed paren)
|
|
echo $?
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `)'
|
|
2
|
|
EOF
|
|
|
|
when_run <<"EOF" "unmatched ) in export"
|
|
export VAR=bon)jour
|
|
echo $?
|
|
echo var=$VAR
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `)'
|
|
2
|
|
var=
|
|
EOF
|
|
|
|
when_run <<"EOF" "unexpected () in export"
|
|
export VAR=bon()jour
|
|
echo $?
|
|
echo var=$VAR
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: syntax error near unexpected token `('
|
|
2
|
|
var=
|
|
EOF
|
|
|
|
when_run <<"EOF" "@ in variable value"
|
|
export VAR=bon@jour
|
|
echo $VAR
|
|
EOF
|
|
expecting <<EOF
|
|
bon@jour
|
|
EOF
|
|
|
|
when_run <<"EOF" "variable substitution in export variable name"
|
|
export VAR=answer
|
|
export the_$VAR=42
|
|
echo the answer is $the_answer
|
|
EOF
|
|
expecting <<EOF
|
|
the answer is 42
|
|
EOF
|
|
|
|
when_run <<"EOF" "variable substitution in export variable name"
|
|
export VAR=answer
|
|
export $VAR=42
|
|
echo the answer is $answer
|
|
EOF
|
|
expecting <<EOF
|
|
the answer is 42
|
|
EOF
|
|
|
|
when_run <<"EOF" "quote removal before word splitting"
|
|
export VAR="hello "
|
|
echo $VAR | cat -e
|
|
EOF
|
|
expecting <<"EOF"
|
|
hello$
|
|
EOF
|
|
|
|
when_run <<"EOF" "quote removal before word splitting"
|
|
export VAR=" hello world "
|
|
echo -$VAR- | cat -e
|
|
EOF
|
|
expecting <<"EOF"
|
|
- hello world -$
|
|
EOF
|
|
|
|
when_run <<"EOF" "quote removal before word splitting"
|
|
export VAR=" hello world "
|
|
echo "-$VAR-" | cat -e
|
|
EOF
|
|
expecting <<"EOF"
|
|
- hello world -$
|
|
EOF
|
|
|
|
when_run <<"EOF" "echo arguments in variables"
|
|
export VAR="-n hello"
|
|
echo $VAR | cat -e
|
|
echo
|
|
EOF
|
|
expecting <<"EOF"
|
|
hello
|
|
EOF
|
|
|
|
when_run <<"EOF" "echo with simple quotes in variables"
|
|
export VAR=" hello' 'world "
|
|
echo "-$VAR-"
|
|
echo -$VAR-
|
|
EOF
|
|
expecting <<"EOF"
|
|
- hello' 'world -
|
|
- hello' 'world -
|
|
EOF
|
|
|
|
when_run <<"EOF" "previous variable value in export"
|
|
export VAR=hello
|
|
export VAR=" $VAR and goodbye"
|
|
echo $VAR | cat -e
|
|
EOF
|
|
expecting <<EOF
|
|
hello and goodbye$
|
|
EOF
|
|
|
|
when_run <<"EOF" "previous variable value in export, but single-quoted"
|
|
export VAR=hello
|
|
export VAR=' $VAR and goodbye'
|
|
echo $VAR | cat -e
|
|
EOF
|
|
expecting <<"EOF"
|
|
$VAR and goodbye$
|
|
EOF
|
|
|
|
when_run <<"EOF" "previous variable value in export, with word joining"
|
|
export VAR=hello
|
|
export VAR=" $VAR and goodbye"$VAR
|
|
echo $VAR | cat -e
|
|
EOF
|
|
expecting <<EOF
|
|
hello and goodbyehello$
|
|
EOF
|
|
|
|
when_run <<"EOF" "command partially in variable"
|
|
touch file1 file2
|
|
export CMD="s -a"
|
|
l$CMD
|
|
EOF
|
|
expecting <<EOF
|
|
.
|
|
..
|
|
file1
|
|
file2
|
|
EOF
|
|
|
|
when_run <<"EOF" "export with extra ="
|
|
export VA=A=hello
|
|
echo $VA
|
|
EOF
|
|
expecting <<EOF
|
|
A=hello
|
|
EOF
|
|
|
|
when_run <<"EOF" "export with extra = and empty string"
|
|
export HOL=A=""
|
|
echo $HOL
|
|
EOF
|
|
expecting <<EOF
|
|
A=
|
|
EOF
|
|
|
|
when_run <<EOF "unset PATH leads to No such file or directory error"
|
|
export EXTRAPATH="$(dirname $(which ls))"
|
|
qwertyuiop
|
|
unset PATH
|
|
qwertyuiop
|
|
export PATH=""
|
|
qwertyuiop
|
|
export PATH=":"
|
|
qwertyuiop
|
|
export PATH="::"
|
|
qwertyuiop
|
|
export PATH="\$EXTRAPATH"
|
|
qwertyuiop
|
|
export PATH=":\$EXTRAPATH"
|
|
qwertyuiop
|
|
export PATH="\$EXTRAPATH:"
|
|
qwertyuiop
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: qwertyuiop: command not found
|
|
minishell: qwertyuiop: No such file or directory
|
|
minishell: qwertyuiop: No such file or directory
|
|
minishell: qwertyuiop: command not found
|
|
minishell: qwertyuiop: command not found
|
|
minishell: qwertyuiop: command not found
|
|
minishell: qwertyuiop: command not found
|
|
minishell: qwertyuiop: command not found
|
|
EOF
|
|
|
|
when_run <<EOF "cd into a file"
|
|
touch file
|
|
cd file
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: cd: file: Not a directory
|
|
1
|
|
EOF
|
|
|
|
when_run <<EOF "subshell"
|
|
touch hi hello
|
|
(ls)
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "subshell within subshell"
|
|
touch hi hello
|
|
((ls))
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "subshell within and"
|
|
touch hi hello
|
|
ls && (ls)
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
hello
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "subshell within or"
|
|
touch hi hello
|
|
(ls) || ls
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "exit within subshell within or"
|
|
touch hi hello
|
|
(exit 1) || ls
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "and withing subshell"
|
|
touch hi hello
|
|
(ls && ls)
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
hello
|
|
hi
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "or withing subshell"
|
|
touch hi hello
|
|
(false || ls)
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "and"
|
|
touch hi hello
|
|
ls && ls
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
hello
|
|
hi
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "or"
|
|
touch hi hello
|
|
false || ls
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
0
|
|
EOF
|
|
|
|
when_run <<"EOF" "unquoting behaviour with wildcard"
|
|
touch outfile
|
|
export target="'out*'"
|
|
echo $target
|
|
ls
|
|
EOF
|
|
expecting <<EOF
|
|
'out*'
|
|
outfile
|
|
EOF
|
|
|
|
when_run <<"EOF" "normal behaviour with wildcard before echo"
|
|
touch outfile
|
|
export target=out*
|
|
echo $target
|
|
EOF
|
|
expecting <<EOF
|
|
outfile
|
|
EOF
|
|
|
|
when_run <<"EOF" "normal behaviour with wildcard after variable expansion"
|
|
touch outfile
|
|
export target="out*"
|
|
echo $target
|
|
EOF
|
|
expecting <<EOF
|
|
outfile
|
|
EOF
|
|
|
|
when_run <<"EOF" "normal behaviour with wildcard before echo where no file corresponds"
|
|
export target=out*
|
|
echo $target
|
|
EOF
|
|
expecting <<EOF
|
|
out*
|
|
EOF
|
|
|
|
when_run <<"EOF" "normal behaviour with wildcard after variable expansion where no file corresponds"
|
|
export target="out*"
|
|
echo $target
|
|
EOF
|
|
expecting <<EOF
|
|
out*
|
|
EOF
|
|
|
|
when_run <<EOF "subshell combination with and"
|
|
((echo hello)&&echo hi)
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "nested subshell combination with and"
|
|
(echo hello&&((echo hi)))
|
|
EOF
|
|
expecting <<EOF
|
|
hello
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "subshell redirection with and"
|
|
(echo hello && echo hi) > outfile
|
|
ls
|
|
cat outfile
|
|
EOF
|
|
expecting <<EOF
|
|
outfile
|
|
hello
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "output redirections with no associated cmd"
|
|
> outfile1 >> outfile2
|
|
ls
|
|
EOF
|
|
expecting <<EOF
|
|
outfile1
|
|
outfile2
|
|
EOF
|
|
|
|
when_run <<EOF "here-doc with no associated cmd"
|
|
<< eof
|
|
hello there
|
|
eof
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "input redirection with no associated cmd"
|
|
echo hello > infile
|
|
< infile
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
0
|
|
EOF
|
|
|
|
when_run <<EOF "input redirection with no associated cmd where infile does not exist"
|
|
< infile
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: infile: No such file or directory
|
|
1
|
|
EOF
|
|
|
|
when_run <<EOF "nested connections"
|
|
(echo hi||pwd)&&(echo hello||pwd)
|
|
EOF
|
|
expecting <<EOF
|
|
hi
|
|
hello
|
|
EOF
|
|
|
|
when_run <<EOF "incorrect command into redirection"
|
|
printff hello > bonjour
|
|
ls
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: printff: command not found
|
|
bonjour
|
|
EOF
|
|
|
|
when_run <<"EOF" "redirection expansion when no commands are included"
|
|
export VAR="bonjour hello"
|
|
>$VAR>hey
|
|
ls
|
|
>hey>$VAR
|
|
ls
|
|
>hey>$VAR>hey>hey
|
|
ls
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: $VAR: ambiguous redirect
|
|
minishell: $VAR: ambiguous redirect
|
|
minishell: $VAR: ambiguous redirect
|
|
EOF
|
|
|
|
when_run <<"EOF" "here-documents limiter is not expanded"
|
|
export LIM=hello
|
|
cat << $LIM
|
|
hi
|
|
hello
|
|
goodbye
|
|
$LIM
|
|
<< $LIM
|
|
EOF
|
|
expecting <<"EOF"
|
|
hi
|
|
hello
|
|
goodbye
|
|
minishell: warning: here-document delimited by end-of-file (wanted `$LIM')
|
|
EOF
|
|
|
|
when_run <<EOF "heredoc does not execute commands"
|
|
cat << eof > bonjour
|
|
echo hello
|
|
cat bonjour
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: warning: here-document delimited by end-of-file (wanted `eof')
|
|
EOF
|
|
|
|
when_run <<EOF "heredoc into another file"
|
|
cat << eof > bonjour
|
|
hello
|
|
there
|
|
eof
|
|
cat bonjour
|
|
EOF
|
|
expecting <<"EOF"
|
|
hello
|
|
there
|
|
EOF
|
|
|
|
when_run <<EOF "heredoc into another command"
|
|
cat << eof | rev
|
|
hello
|
|
there
|
|
eof
|
|
EOF
|
|
expecting <<"EOF"
|
|
olleh
|
|
ereht
|
|
EOF
|
|
|
|
when_run <<EOF "heredoc all alone"
|
|
<< eof
|
|
it's just me
|
|
by myself
|
|
i'm all alone ;.;
|
|
eof
|
|
EOF
|
|
expecting <<"EOF"
|
|
EOF
|
|
|
|
when_run <<EOF "TRIPLE SUMMON"
|
|
cat<<x<<y<<z
|
|
hello
|
|
x
|
|
there
|
|
y
|
|
hi
|
|
z
|
|
EOF
|
|
expecting <<EOF
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "TRIPLE SUMMON into unknown command"
|
|
qwertyuiop<<x<<y<<z
|
|
hello
|
|
x
|
|
there
|
|
y
|
|
hi
|
|
z
|
|
echo \$?
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: qwertyuiop: command not found
|
|
127
|
|
EOF
|
|
|
|
when_run <<EOF "simple quote removal for heredoc-limiters"
|
|
cat << 'hola'
|
|
test
|
|
hola
|
|
EOF
|
|
expecting <<EOF
|
|
test
|
|
EOF
|
|
|
|
when_run <<EOF "double quote removal for heredoc-limiters"
|
|
cat << "hola"
|
|
test
|
|
hola
|
|
EOF
|
|
expecting <<EOF
|
|
test
|
|
EOF
|
|
|
|
when_run <<EOF "nested quote removal for heredoc-limiters"
|
|
cat << "'hola'"
|
|
test
|
|
'hola'
|
|
EOF
|
|
expecting <<EOF
|
|
test
|
|
EOF
|
|
|
|
when_run <<EOF "absent quoted limiter for heredoc-limiters"
|
|
cat << "'hola'"
|
|
test
|
|
EOF
|
|
expecting <<"EOF"
|
|
minishell: warning: here-document delimited by end-of-file (wanted `'hola'')
|
|
test
|
|
EOF
|
|
|
|
when_run <<EOF "exit in pipeline"
|
|
exit 10 | echo hi
|
|
EOF
|
|
expecting <<EOF
|
|
hi
|
|
EOF
|
|
|
|
when_run <<EOF "wildcard redirect ambiguous"
|
|
touch a.txt b.txt
|
|
echo hello > *.txt
|
|
EOF
|
|
expecting <<EOF
|
|
minishell: *.txt: ambiguous redirect
|
|
EOF
|
|
|
|
finalize
|