mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Merge #5006
5006: Add integration test script and coverage map r=heinrich5991 a=ChillerDragon The ``scripts/integration_test.sh`` runs two clients and one server. Lets the two clients connect for a few seconds. The goal is to get some automated runtime tests. Even tho the the two clients do not move they will touch a few gametiles by falling through them. This covers a few basic game mechanics like: freeze, draggers, bullets, solo, shields, tele, speedup, collision, player collision and map finishes. This pr adds a new directory ``test/`` at the root of the repository. Holding a test map. (not sure if thats cool 🤷 ) The script can be run locally using ``scripts/integration_test.sh [build directory]`` using ``build`` as a default. It will catch client/server crashes and check if sqlite ranks are inserted. If built with asan/ubsan it will catch these errors too. Probably not too useful for local testing since it most likely will not cover the currently developed features. In the CI it will use a ASan + UBSan build directory and thus catch all basic issues caused by simple client server interaction. ![image](https://user-images.githubusercontent.com/20344300/163870637-acc5b16d-042b-407a-b223-febcd2565c97.png) Co-authored-by: Dennis Felsing <dennis@felsin9.de> Co-authored-by: ChillerDrgon <ChillerDragon@gmail.com>
This commit is contained in:
commit
8e3d5c1b59
3
.github/workflows/clang-sanitizer.yml
vendored
3
.github/workflows/clang-sanitizer.yml
vendored
|
@ -43,3 +43,6 @@ jobs:
|
|||
cat ./SAN.*
|
||||
exit 1
|
||||
fi
|
||||
- name: Run integration tests with ASan and UBSan
|
||||
run: |
|
||||
./scripts/integration_test.sh san
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -78,6 +78,7 @@ generated
|
|||
target
|
||||
|
||||
/build*
|
||||
/integration_test/*
|
||||
|
||||
# IDE project files
|
||||
.cache
|
||||
|
|
6
integration_test/README.md
Normal file
6
integration_test/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# tests
|
||||
|
||||
Use the test script at the root of the repo to run tests
|
||||
|
||||
./scripts/integration_test.sh
|
||||
|
BIN
integration_test/maps/coverage.map
Normal file
BIN
integration_test/maps/coverage.map
Normal file
Binary file not shown.
222
scripts/integration_test.sh
Executable file
222
scripts/integration_test.sh
Executable file
|
@ -0,0 +1,222 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ ! -f ./scripts/integration_test.sh ] || [ ! -f CMakeLists.txt ]
|
||||
then
|
||||
echo "Error: make sure your are in the root of the repo"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
arg_build_dir="build"
|
||||
arg_end_args=0
|
||||
arg_verbose=0
|
||||
|
||||
for arg in "$@"
|
||||
do
|
||||
if [[ "${arg::1}" == "-" ]] && [[ "$arg_end_args" == "0" ]]
|
||||
then
|
||||
if [ "$arg" == "-h" ] || [ "$arg" == "--help" ]
|
||||
then
|
||||
echo "usage: $(basename "$0") [OPTION..] [build dir]"
|
||||
echo "description:"
|
||||
echo " Runs a simple integration test of the client and server"
|
||||
echo " binaries from the given build dir"
|
||||
echo "options:"
|
||||
echo " --help|-h show this help"
|
||||
echo " --verbose|-v verbose output"
|
||||
elif [ "$arg" == "-v" ] || [ "$arg" == "--verbose" ]
|
||||
then
|
||||
arg_verbose=1
|
||||
elif [ "$arg" == "--" ]
|
||||
then
|
||||
arg_end_args=1
|
||||
else
|
||||
echo "Error: unknown arg '$arg'"
|
||||
fi
|
||||
else
|
||||
arg_build_dir="$arg"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -d "$arg_build_dir" ]
|
||||
then
|
||||
echo "Error: build directory '$arg_build_dir' not found"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$arg_build_dir"/DDNet ]
|
||||
then
|
||||
echo "Error: client binary not found '$arg_build_dir/DDNet' not found"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$arg_build_dir"/DDNet-Server ]
|
||||
then
|
||||
echo "Error: server binary not found '$arg_build_dir/DDNet-Server' not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p integration_test
|
||||
cp "$arg_build_dir"/DDNet* integration_test
|
||||
|
||||
cd integration_test || exit 1
|
||||
|
||||
function kill_all() {
|
||||
if [ "$arg_verbose" == "1" ]
|
||||
then
|
||||
echo "[*] shutting down test clients and server ..."
|
||||
fi
|
||||
sleep 1
|
||||
echo "shutdown" > server.fifo
|
||||
echo "quit" > client1.fifo
|
||||
echo "quit" > client2.fifo
|
||||
}
|
||||
|
||||
got_cleanup=0
|
||||
|
||||
function cleanup() {
|
||||
# needed to fix hang fifo with additional ctrl+c
|
||||
if [ "$got_cleanup" == "1" ]
|
||||
then
|
||||
exit
|
||||
fi
|
||||
got_cleanup=1
|
||||
kill_all
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
{
|
||||
echo $'add_path $CURRENTDIR'
|
||||
echo $'add_path $USERDIR'
|
||||
echo $'add_path $DATADIR'
|
||||
echo $'add_path ../data'
|
||||
} > storage.cfg
|
||||
|
||||
function fail()
|
||||
{
|
||||
sleep 1
|
||||
tail -n2 "$1".log > fail_"$1".txt
|
||||
echo "$1 exited with code $2" >> fail_"$1".txt
|
||||
echo "[-] $1 exited with code $2"
|
||||
}
|
||||
|
||||
if test -n "$(find . -maxdepth 1 -name '*.fifo' -print -quit)"
|
||||
then
|
||||
rm ./*.fifo
|
||||
fi
|
||||
if test -n "$(find . -maxdepth 1 -name 'SAN.*' -print -quit)"
|
||||
then
|
||||
rm SAN.*
|
||||
fi
|
||||
if test -n "$(find . -maxdepth 1 -name 'fail_*' -print -quit)"
|
||||
then
|
||||
rm ./fail_*
|
||||
fi
|
||||
if [ -f ddnet-server.sqlite ]
|
||||
then
|
||||
rm ddnet-server.sqlite
|
||||
fi
|
||||
|
||||
# TODO: check for open ports instead
|
||||
port=17822
|
||||
|
||||
cp ../ubsan.supp .
|
||||
|
||||
if [[ $OSTYPE == 'darwin'* ]]; then
|
||||
DETECT_LEAKS=0
|
||||
else
|
||||
DETECT_LEAKS=1
|
||||
fi
|
||||
|
||||
export UBSAN_OPTIONS=suppressions=./ubsan.supp:log_path=./SAN:print_stacktrace=1:halt_on_errors=0
|
||||
export ASAN_OPTIONS=log_path=./SAN:print_stacktrace=1:check_initialization_order=1:detect_leaks=$DETECT_LEAKS:halt_on_errors=0
|
||||
|
||||
function print_san() {
|
||||
if test -n "$(find . -maxdepth 1 -name 'SAN.*' -print -quit)"
|
||||
then
|
||||
cat ./SAN.*
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
./DDNet-Server \
|
||||
"sv_input_fifo server.fifo;
|
||||
sv_map coverage;
|
||||
sv_sqlite_file ddnet-server.sqlite;
|
||||
sv_port $port" &> server.log || fail server "$?" &
|
||||
|
||||
./DDNet \
|
||||
"cl_input_fifo client1.fifo;
|
||||
player_name client1;
|
||||
cl_download_skins 0;
|
||||
connect localhost:$port" &> client1.log || fail client1 "$?" &
|
||||
|
||||
sleep 0.5
|
||||
|
||||
./DDNet \
|
||||
"cl_input_fifo client2.fifo;
|
||||
player_name client2;
|
||||
cl_download_skins 0;
|
||||
connect localhost:$port" &> client2.log || fail client2 "$?" &
|
||||
|
||||
fails=0
|
||||
# give the client time to launch and create the fifo file
|
||||
# but assume after 3 secs that the client crashed before
|
||||
# being able to create the file
|
||||
while [[ ! -p client1.fifo ]]
|
||||
do
|
||||
fails="$((fails+1))"
|
||||
if [ "$arg_verbose" == "1" ]
|
||||
then
|
||||
echo "[!] client fifo not found (attempts $fails/3)"
|
||||
fi
|
||||
if [ "$fails" -gt "2" ]
|
||||
then
|
||||
print_san
|
||||
echo "[-] Error: client possibly crashed on launch"
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
sleep 2
|
||||
|
||||
kill_all
|
||||
wait
|
||||
|
||||
sleep 1
|
||||
|
||||
ranks="$(sqlite3 ddnet-server.sqlite < <(echo "select * from record_race;"))"
|
||||
num_ranks="$(echo "$ranks" | wc -l | xargs)"
|
||||
if [ "$ranks" == "" ]
|
||||
then
|
||||
touch fail_ranks.txt
|
||||
echo "[-] Error: no ranks found in database"
|
||||
elif [ "$num_ranks" != "1" ]
|
||||
then
|
||||
touch fail_ranks.txt
|
||||
echo "[-] Error: expected 1 rank got $num_ranks"
|
||||
elif ! echo "$ranks" | grep -q client1
|
||||
then
|
||||
touch fail_ranks.txt
|
||||
echo "[-] Error: expected a rank from client1 instead got:"
|
||||
echo " $ranks"
|
||||
fi
|
||||
|
||||
if test -n "$(find . -maxdepth 1 -name 'fail_*' -print -quit)"
|
||||
then
|
||||
if [ "$arg_verbose" == "1" ]
|
||||
then
|
||||
for fail in ./fail_*
|
||||
do
|
||||
cat "$fail"
|
||||
done
|
||||
fi
|
||||
print_san
|
||||
echo "[-] Test failed. See errors above."
|
||||
exit 1
|
||||
else
|
||||
echo "[*] all tests passed"
|
||||
fi
|
||||
|
||||
print_san || exit 1
|
|
@ -124,14 +124,17 @@ inline float angle(const vector2_base<float> &a)
|
|||
template<typename T>
|
||||
inline vector2_base<T> normalize_pre_length(const vector2_base<T> &v, T len)
|
||||
{
|
||||
if(len == 0)
|
||||
return vector2_base<T>();
|
||||
return vector2_base<T>(v.x / len, v.y / len);
|
||||
float l = (float)(1.0f / sqrtf(v.x * v.x + v.y * v.y));
|
||||
return vector2_base<float>(v.x * l, v.y * l);
|
||||
}
|
||||
|
||||
inline vector2_base<float> normalize(const vector2_base<float> &v)
|
||||
{
|
||||
float l = (float)(1.0f / sqrtf(v.x * v.x + v.y * v.y));
|
||||
float divisor = sqrtf(v.x * v.x + v.y * v.y);
|
||||
if(divisor == 0.0f)
|
||||
return vector2_base<float>(0.0f, 0.0f);
|
||||
float l = (float)(1.0f / divisor);
|
||||
return vector2_base<float>(v.x * l, v.y * l);
|
||||
}
|
||||
|
||||
|
@ -273,7 +276,10 @@ inline float length(const vector3_base<float> &a)
|
|||
|
||||
inline vector3_base<float> normalize(const vector3_base<float> &v)
|
||||
{
|
||||
float l = (float)(1.0f / sqrtf(v.x * v.x + v.y * v.y + v.z * v.z));
|
||||
float divisor = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
if(divisor == 0.0f)
|
||||
return vector3_base<float>(0.0f, 0.0f, 0.0f);
|
||||
float l = (float)(1.0f / divisor);
|
||||
return vector3_base<float>(v.x * l, v.y * l, v.z * l);
|
||||
}
|
||||
|
||||
|
|
|
@ -144,12 +144,14 @@ void CGameWorld::RemoveEntity(CEntity *pEnt)
|
|||
|
||||
if(pEnt->m_ObjType == ENTTYPE_CHARACTER)
|
||||
{
|
||||
CCharacter *pChar = (CCharacter *)pEnt;
|
||||
int ID = pChar->GetCID();
|
||||
if(ID >= 0 && ID < MAX_CLIENTS)
|
||||
if(CCharacter *pChar = dynamic_cast<CCharacter *>(pEnt))
|
||||
{
|
||||
m_apCharacters[ID] = 0;
|
||||
m_Core.m_apCharacters[ID] = 0;
|
||||
int ID = pChar->GetCID();
|
||||
if(ID >= 0 && ID < MAX_CLIENTS)
|
||||
{
|
||||
m_apCharacters[ID] = 0;
|
||||
m_Core.m_apCharacters[ID] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue