tests: implement a better integration test framework than shellspec

(for our usecase at least)
This commit is contained in:
Khaïs COLIN 2025-03-19 15:11:32 +01:00
parent 1f03cbbedb
commit 36c1b72eff
Signed by: logistic-bot
SSH key fingerprint: SHA256:RlpiqKeXpcPFZZ4y9Ou4xi2M8OhRJovIwDlbCaMsuAo
6 changed files with 148 additions and 75 deletions

1
.gitignore vendored
View file

@ -33,3 +33,4 @@ bash.txt
.cache/ .cache/
compile_commands.json compile_commands.json
.vscode .vscode
report.txt

View file

@ -1,12 +0,0 @@
--require spec_helper
## Default kcov (coverage) options
# --kcov-options "--include-path=. --path-strip-level=1"
# --kcov-options "--include-pattern=.sh"
# --kcov-options "--exclude-pattern=/.shellspec,/spec/,/coverage/,/report/"
## Example: Include script "myprog" with no extension
# --kcov-options "--include-pattern=.sh,myprog"
## Example: Only specified files/directories
# --kcov-options "--include-pattern=myprog,/lib/"

View file

@ -82,7 +82,8 @@ minishell_objs = $(addsuffix .o,src/$(NAME)) $(objs)
all_objs = $(minishell_objs) all_objs = $(minishell_objs)
deps = $(all_objs:.o=.d) deps = $(all_objs:.o=.d)
.PHONY: all clean fclean re norm tests ctests ctestsa ctestsub ctestst alla allub allt .PHONY: all clean fclean re norm tests ctests ctestsa ctestsub ctestst alla \
allub allt testsa testsub testst inttests inttestsa inttestsub inttestst
all: $(NAME) all: $(NAME)
@ -116,24 +117,48 @@ norm:
norminette src libft | grep -v OK || true norminette src libft | grep -v OK || true
tests: tests:
@echo "Running tests with AddressSanitizer..." +make fclean
+CFLAGS="$(CFLAGS) $(ASAN)" make re +make testsa
shellspec +make fclean
+make ctestsa +make testsub
+make fclean
@echo "Running tests with UndefinedBehaviourSanitizer..." +make testst
+CFLAGS="$(CFLAGS) $(UBSAN)" make re
shellspec
+make ctestsub
@echo "Running tests with ThreadSanitizer..."
+CFLAGS="$(CFLAGS) $(TSAN)" make re
shellspec
+make ctestst
@echo "All tests passed!" @echo "All tests passed!"
ctests: $(LIBFT) testsa: CFLAGS += $(ASAN)
testsa:
@echo "Running tests with AddressSanitizer..."
+make inttests
+make ctests
testsub: CFLAGS += $(UBSAN)
testsub: all
@echo "Running tests with UndefinedBehaviourSanitizer..."
+make inttests
+make ctests
testst: CFLAGS += $(TSAN)
testst: all
@echo "Running tests with ThreadSanitizer..."
+make inttests
+make ctests
inttests: all
./test.sh
inttestsa: CFLAGS += $(ASAN)
inttestsa: all
./test.sh
inttestsub: CFLAGS += $(UBSAN)
inttestsub: all
./test.sh
inttestst: CFLAGS += $(TSAN)
inttestst: all
./test.sh
ctests: $(LIBFT) all
+make -C tests +make -C tests
ctestsa: CFLAGS += $(ASAN) ctestsa: CFLAGS += $(ASAN)

View file

@ -1,22 +0,0 @@
It "parses null for empty command"
Data
#|
End
When call ./minishell
The output should eq ""
End
It "a single command is parsed"
Data
#|echo hello
End
When call ./minishell
The output should eq \
" ╰─ t_cmdgroup
├─ t_cmdlist
│ ├─ num_cmds = 1
│ ╰─ cmd[0]
╰─ t_redir_list"
End

View file

@ -1,24 +0,0 @@
# shellcheck shell=sh
# Defining variables and functions here will affect all specfiles.
# Change shell options inside a function may cause different behavior,
# so it is better to set them here.
# set -eu
# This callback function will be invoked only once before loading specfiles.
spec_helper_precheck() {
# Available functions: info, warn, error, abort, setenv, unsetenv
# Available variables: VERSION, SHELL_TYPE, SHELL_VERSION
: minimum_version "0.28.1"
}
# This callback function will be invoked after a specfile has been loaded.
spec_helper_loaded() {
:
}
# This callback function will be invoked after core modules has been loaded.
spec_helper_configure() {
# Available functions: import, before_each, after_each, before_all, after_all
: import 'support/custom_matcher'
}

105
test.sh Executable file
View file

@ -0,0 +1,105 @@
#!/usr/bin/env bash
set -uo pipefail
declare -i FAILED=0
declare -i SUCCEDED=0
declare -i RAN=0
NAME=""
failed_report()
{
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo $NAME
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo "++++ Failed test:"
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:"
echo "echo '$(cat /tmp/input.minishell)' | ./minishell"
}
failed()
{
failed_report >> report.txt
echo -n "F"
FAILED+=1
}
succeded()
{
echo -n "."
SUCCEDED+=1
}
assert()
{
diff -q /tmp/got.minishell /tmp/expected.minishell > /dev/null && succeded || failed
}
when_run()
{
cat > /tmp/input.minishell
./minishell &> /tmp/got.minishell < /tmp/input.minishell
RAN+=1
NAME=$1
}
expecting()
{
cat > /tmp/expected.minishell
assert
}
setup()
{
rm -f report.txt
}
finalize()
{
echo
echo "$RAN tests ran. $SUCCEDED succeded, $FAILED failed."
if [ $SUCCEDED -eq $RAN ];
then
echo "All integration tests passed!"
else
echo "cat report.txt"
echo "to see details of failed tests"
fi
}
setup
when_run <<EOF "empty inputs returns nothing"
EOF
expecting <<EOF
EOF
when_run <<EOF "simple command is parsed"
echo hello
EOF
expecting <<EOF
╰─ t_cmdgroup
├─ t_cmdlist
│ ├─ num_cmds = 1
│ ╰─ cmd[0]
│ ├─ t_cmdlist_item
│ │ ╰─ t_pipeline
│ │ ├─ num_cmd = 1
│ │ ╰─ cmd[0]
│ │ ╰─ t_simple_cmd
│ │ ├─ words = [echo][hello]
│ │ ╰─ (no redirections)
│ ╰─ t_operator = END
╰─ (no redirections)
EOF
finalize